Kaip sujungti du git saugyklos?

Apsvarstykite šį scenarijų:

Aš sukūriau nedidelį eksperimentinį A projektą savo „Git“ repo. Dabar ji yra subrendusi ir norėčiau, kad A būtų didesnio projekto B dalis, turinti didelę saugyklą. Dabar norėčiau A pridėti kaip „B“ pakatalogį.

Kaip sujungti A į B, neprarandant abiejų pusių istorijos?

1335
15 сент. set static_rtti Rugsėjo 15 d 2009-09-15 11:31 '09 11:31 val. 2009-09-15 11:31
@ 22 atsakymai

Vieną kitos saugyklos filialą galima lengvai įdėti į pakatalogį, kuris išsaugo jos istoriją. Pavyzdžiui:

 git subtree add --prefix=rails git://github.com/rails/rails.git master 

Tai bus rodoma kaip vienas įvykis, kai visi pagrindiniai „Rails“ filialo failai bus įtraukti į katalogą „bėgiai“. Tačiau paskyrimo antraštėje yra nuoroda į senąjį istorijos medį:

Pridėti „rails /“ iš įsipareigojimo <rev>

Kur <rev> yra SHA-1 maišos kodas. Jūs vis dar galite pamatyti istoriją, kaltinti kai kuriuos pakeitimus.

 git log <rev> git blame <rev> -- README.md 

Atkreipkite dėmesį, kad čia negalite matyti katalogo prefikso, nes tai yra tikrasis senas filialas, paliktas nepaliestas. Turėtumėte tai peržiūrėti kaip įprastą failo judesio nustatymą: jums reikės papildomo šuolio, kai jį pasieksite.

 # finishes with all files added at once commit git log rails/README.md # then continue from original tree git log <rev> -- README.md 

Yra sudėtingesnių sprendimų, pvz., Rankiniu būdu arba perrašant istoriją, kaip aprašyta kituose atsakymuose.

„Git-subtree“ komanda yra oficialios „git-init“ dalis, kai kurie paketų tvarkytojai įdiegia pagal nutylėjimą (OS X Homebrew). Bet jums gali tekti ją įdiegti be git.

350
21 февр. Simon Perepelitsa atsakymas 21 vasaris 2013-02-21 02:44 '13, 02:44 2013-02-21 02:44

Jei norite sujungti project-a su project-b :

Git-git“ sujungimo ir (arba) nesusijusios istorijos dokumentus 

Atnaujinti : pridėtos - --tags , kurias pasiūlė @jstadler, kad išsaugotumėte žymes.

1443
11 мая '12 в 12:37 2012-05-11 12:37 atsakymą pateikė Andresch Serj gegužės 11 d., 12 val. 12:37 2012-05-11 12:37

Čia pateikiami du galimi sprendimai:

submoduliai

Nukopijuokite saugyklą A į atskirą katalogą didesniame projekte B arba (galbūt geriau) klonuoti saugyklą A į projektą B esančią pakatalogį. Tada naudokite dievų submodulį , kad šis saugykla taptų B saugyklos poskyriu.

Tai geras sprendimas laisvai sujungtoms saugykloms, kur A plėtra tęsiasi, o didžioji dalis plėtros yra atskira atskira plėtra A zonoje. Taip pat žiūrėkite „ SubmoduleSupport“ ir „ GitSubmoduleTutorial“ puslapius „Git Wiki“.

Subfree sintezė

Galima sujungti saugyklą A į projekto B pakategorę, naudodama subtree susijungimo strategiją. Tai aprašyta „ Subtree Merging“ ir „Markus Prinz“.

 git remote add -f Bproject /path/to/B git merge -s ours --allow-unrelated-histories --no-commit Bproject/master git read-tree --prefix=dir-B/ -u Bproject/master git commit -m "Merge B project as our subdirectory" git pull -s subtree Bproject master 

(Gitui> = 2.9.0, reikalinga galimybė - - nesuderintos --allow-unrelated-histories ).

Arba galite naudoti „ git subtree“ įrankį ( „GitHub“ saugyklą ), kurį apenwarr („Avery Pennarun“) paskelbė, pavyzdžiui, savo dienoraštyje . Nauja alternatyva „Git: git“ submoduliams .


Manau, kad jūsų atveju (A turėtų būti didesnio projekto B dalis) teisingas sprendimas būtų naudoti subtree susijungimą .

587
15 сент. Jakub Narębski atsakymas rugsėjo 15 d 2009-09-15 12:38 '09, 12:38, 2009-09-15 12:38

Submodulinis požiūris yra geras, jei norite paremti projektą atskirai. Tačiau, jei tikrai norite sujungti abu projektus į tą pačią saugyklą, turite šiek tiek dirbti.

Pirmas dalykas, kuris būtų naudoti „ git filter-branch yra perrašyti visų pavadinimų pavadinimus antrajame saugykloje į antrinį katalogą, kuriame norėtumėte juos baigti. Todėl vietoj foo.c , bar.html turėsite projb/foo.c ir projb/bar.html .

Tada galite padaryti kažką panašaus:

 git remote add projb [wherever] git pull projb 

git pull atliks „ git fetch ir git merge . Neturėtų būti jokių konfliktų, jei saugykloje, į kurią projb/ dar nėra projb/ katalogo.

Tolesnė paieška rodo, kad kažkas panašaus buvo padaryta siekiant sujungti gitk į git . Junio ​​C Hamano apie tai rašo: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html

191
15 сент. Greg Hewgill atsakymas rugsėjo 15 d 2009-09-15 11:38 '09 11:38 val 2009-09-15 11:38

git-subtree yra gera, bet tikriausiai ne tas, ko jums reikia.

Pavyzdžiui, jei projectA yra katalogas, sukurtas B, po git subtree ,

 git log projectA 

išvardija tik vieną įvykį: sujungti. Sujungto projekto įsipareigojimai skirti skirtingiems keliams, todėl jie nerodomi.

Greg Hewgill atsakymas yra artimas, nors iš tiesų jis nesako, kaip perrašyti kelią.


Sprendimas yra stebėtinai paprastas.

(1) A,

 PREFIX=projectA #adjust this git filter-branch --index-filter ' git ls-files -s | sed "s,\t, | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info  mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE ' HEAD 

Pastaba Tai perrašo istoriją, todėl, jei ketinate toliau naudoti šį repo A, galite pirmiausia kopijuoti (kopijuoti) savo trumpalaikę kopiją.

(2) Tada B bėgyje

 git pull path/to/A 

Voila! Jūs turite projectA katalogą. Jei paleisite git log projectA , pamatysite, kad visi įsipareigojimai yra iš A.


Mano atveju man reikia dviejų pakatalogių, projectA ir projectB . Šiuo atveju aš taip pat ėmiau (1) žingsnį B.

63
01 февр. atsakymą pateikė Paul Draper 01 vasaris. 2014-02-01 11:10 '14 at 11:10 2014-02-01 11:10

Jei abiejose saugyklose yra tie patys failai (pvz., Du „Rails“ saugyklos skirtingiems projektams), antrinės saugyklos duomenis galite gauti į dabartinę saugyklą:

  git fetch git://repository.url/repo.git master: имя_файла Код> 

ir tada sujungti jį į dabartinę saugyklą:

  git merge --allow-unrelated-historys branch_name Код> 

Jei jūsų „Git“ versija yra mažesnė nei 2.9, ištrinkite - allow-unrelated-history .

Po to gali kilti konfliktai. Galite juos įgalinti, pavyzdžiui, naudodami git mergetool . kdiff3 gali būti naudojamas tik su klaviatūra, todėl, skaitant kodą, vos per kelias minutes trunka tik 5 konfliktinius failus.

Nepamirškite užbaigti sujungimo:

  <code> git įsipareigoja Kodas> 
43
11 июня '11 в 17:31 2011-06-11 17:31 atsakymą pateikė Smar , birželio 11 d. 11, 17:31 2011-06-11 17:31

Toliau grojau istoriją, kai naudoju susijungimą, taigi aš naudoju perskaičiavimą, nes mano atveju abu saugyklos skiriasi viena nuo kitos, kad nebūtų sujungtos su kiekvienu įsipareigojimu:

 git clone git@gitorious/projA.git projA git clone git@gitorious/projB.git projB cd projB git remote add projA ../projA/ git fetch projA git rebase projA/master HEAD 

=> išspręsti konfliktus ir tada tęsti, tiek kartų, kiek reikia ...

 git rebase --continue 

Tokiu būdu vienas projektas turi visus projektus iš projektinio projekto, o po to - iš projekto

22
27 февр. Atsakymą pateikė Calahad 27 vasario mėn. 2014-02-27 06:09 '14 at 6:09 2014-02-27 06:09

Mano atveju, turėjau my-plugin saugyklą „ my-plugin ir main-project saugyklos main-project , ir aš norėjau apsimesti, kad my-plugin visada my-plugin sukurtas „ plugins main-project .

Iš esmės, aš perrašiau my-pluginmy-plugin saugyklos istoriją, kad pasirodytų, visa plėtra įvyko „ plugins/my-plugin pakatalogyje. Tada pridėjau main-project my-plugin plėtros istoriją ir kartu sujungiau du medžius. Kadangi „ plugins/my-plugin katalogas nebebuvo „plug-ins plugins/my-plugin main-project saugykloje, tai buvo trivialus „-c onfactions“ suliejimo nebuvimas. Susidariusioje saugykloje buvo visa pradinių projektų istorija ir buvo dvi šaknys.

Tl; DR

 $ cp -R my-plugin my-plugin-dirty $ cd my-plugin-dirty $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob  setopt glob_dots  mkdir -p plugins/my-plugin  (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all $ cd ../main-project $ git checkout master $ git remote add --fetch my-plugin ../my-plugin-dirty $ git merge my-plugin/master --allow-unrelated-histories $ cd .. $ rm -rf my-plugin-dirty 

Ilga versija

Pirmiausia sukurkite my-plugin saugyklos kopiją, nes ketiname perrašyti šio saugyklos istoriją.

Dabar eikite į my-plugin saugyklos šaknį, patikrinkite pagrindinį filialą (galbūt „ master ) ir paleiskite šią komandą. Žinoma, turėtumėte pakeisti my-plugin ir plugins bet kuriuo iš jūsų faktinių pavadinimų.

 $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob  setopt glob_dots  mkdir -p plugins/my-plugin  (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all 

Dabar paaiškinkite. git filter-branch --tree-filter (...) HEAD komandą (...) kiekvienam git filter-branch --tree-filter (...) HEAD galima gauti iš HEAD . Atkreipkite dėmesį, kad tai daro poveikį kiekvienam įsipareigojimui tiesiogiai saugomiems duomenims, todėl nereikia jaudintis dėl terminų darbo katalogas, indeksas, etapas ir kt.

Jei paleisite filter-branch komandą, kuri nepavyksta, ji palieka kai kuriuos failus .git kataloge ir kitą kartą bandydami filter-branch ji skundžiasi, jei nepateiksite -f filter-branch .

Kalbant apie tikrąją komandą, aš nesisekė gauti bash , ką norėjau, todėl aš naudoju zsh -c , kad zsh atliktų komandą. Pirma, nustatysiu parametrą glob_dots , kuris leidžia naudoti ^(...) sintaksę komandoje mv , taip pat parametrą glob_dots , kuris leidžia man pasirinkti dotfiles (pvz., .gitignore ) naudojant glob ( ^(...) ).

Tada naudoju komandą mkdir -p kad tuo pačiu metu sukurtume ir plugins ir plugins/my-plugin .

Galiausiai naudoju zsh funkciją „neigiamas globas“ ^(.git|plugins) kad atitiktų visus failus, esančius saugyklos šakniniame kataloge, išskyrus .git ir naujai sukurtą my-plugin . (Išskyrus .git gali būti nebūtina, tačiau bandymas perkelti katalogą į save yra klaida.)

Mano saugykloje originalus įsipareigojimas neįtraukė jokių failų, taigi mv komanda sugrąžino klaidą per pradinį įvykį (nes nieko nebuvo galima perkelti). Taigi pridėjau || true || true || true || true kad „ git filter-branch nepertraukiamas.

Pasirinkimas „ --all parodo, kad filter-branch perrašo visų saugyklų filialų istoriją ir papildomą parinktį, kaip pasakyti, kad „ git interpretuoti kaip dalį filialų perrašymo sąrašo, o ne pačią parinktį, skirtą filter-branch .

Dabar eikite į main-project saugyklos main-project ir patikrinkite, kurį filialą norite sujungti. Pridėkite my-plugin saugyklos kopiją „ my-plugin my-plugin (su pakeista istorija) kaip nuotolinį main-project su:

 $ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY 

Dabar įsipareigojimo istorijoje turėsite du nesusijusius medžius, kuriuos galite gerai atvaizduoti naudodami:

 $ git log --color --graph --decorate --all 

Jei norite juos sujungti, naudokite:

 $ git merge my-plugin/master --allow-unrelated-histories 

Atkreipkite dėmesį, kad prieš 2.9.0 gitą trūksta parametro - „all-non-related --allow-unrelated-histories . Jei naudojate vieną iš šių versijų, paprasčiausiai nepamirškite šios parinkties: klaidos pranešimas, kad ir 2.9.0.

Jūs neturėtumėte sujungti konfliktų. Jei tai padarysite, tikriausiai tai reiškia, kad filter-branch komanda neveikė tinkamai, arba „ plugins/my-plugin katalogas jau buvo main-project plugins/my-plugin main-project plugins/my-plugin main-project .

Būtinai įveskite aiškinamąjį pranešimą apie būsimus autorius, kurie domisi įsilaužimu, kad sukurtų dviejų šaknų saugyklą.

Galite vizualizuoti naują git log grafiką, kuriame turėtų būti dvi šaknų komandos, naudojant aukščiau nurodytą git log komandą. Atminkite, kad bus sujungtas tik master filialas . Tai reiškia, kad jei turite svarbų darbą su kitu my-plugin , kurį norite sujungti į main-project medį, turėtumėte susilaikyti nuo my-plugin nuotolinio valdymo, kol nebaigsite šių susijungimų. Jei to nepadarysite, šių filialų įsipareigojimai vis dar bus main-project saugyklose, tačiau kai kurie iš jų bus neprieinami ir gali būti nulemti atliekų surinkimą. (Taip pat turėsite prieiti prie jų naudodami SHA, nes nuotolinio ištrynimo metu pašalinami nuotolinio stebėjimo filialai.)

Jei norite sujungti viską, ką norite išsaugoti nuo my-plugin , galite pašalinti nuotolinio valdymo pultą naudodami:

 $ git remote remove my-plugin 

Dabar galite saugiai ištrinti my-plugin saugyklos kopiją, kurios istorija pasikeitė. Mano atveju, po to, kai buvo sujungtas ir spustelėjęs, pridėjau pranešimą apie tikrąjį my-plugin saugyklą „ my-plugin .


Išbandyta „Mac OS X El Capitan“ su git --version 2.9.0 ir zsh --version 5.2 git --version 2.9.0 zsh --version 5.2 . Jūsų rida gali skirtis.

Rekomendacijos:

15
19 авг. atsakymą pateikė Radon Rosborough 19 rug . 2016-08-19 00:06 '16 at 0:06 2016-08-19 00:06

Aš bandau tai daryti keletą dienų, naudoju git 2.7.2. Subtree nesaugo istorijos.

Šis metodas gali būti naudojamas, jei dar kartą nenaudojate senojo projekto.

Siūlau, kad jūs pirmiausia patektumėte į B ir dirbate filiale.

Toliau pateikiami veiksmai be šakos:

 cd B # You are going to merge A into B, so first move all of B files into a sub dir mkdir B # Move all files to B, till there is nothing in the dir but .git and B git mv <files> B git add . git commit -m "Moving content of project B in preparation for merge from A" # Now merge A into B git remote add -f A <A repo url> git merge A/<branch> mkdir A # move all the files into subdir A, excluding .git git mv <files> A git commit -m "Moved A into subdir" # Move B files back to root git mv B}" declare SCRIPT_DIR="$(cd ${0%} sub_project=${sub_project%*.git} echo -e "INFO: Project ${sub_project}" echo -e "INFO: Project ${sub_project}" >>${LOG_FILE} 2> echo -e "----------------------------------------------------" echo -e "----------------------------------------------------" >>${LOG_FILE} 2> # Fetch the project echo -e "INFO: Fetching ${sub_project}..." echo -e "INFO: Fetching ${sub_project}..." >>${LOG_FILE} 2> git remote add "${sub_project}" "${url}" if ! git fetch --tags --quiet ${sub_project} >>${LOG_FILE} 2> ; then failed "Failed to fetch project ${sub_project}" fi # add remote branches echo -e "INFO: Creating local branches for ${sub_project}..." echo -e "INFO: Creating local branches for ${sub_project}..." >>${LOG_FILE} 2> while read branch ; do branch_ref=$(echo $branch | tr " " "\t" | cut -f 1) branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-) echo -e "INFO: Creating branch ${branch_name}..." echo -e "INFO: Creating branch ${branch_name}..." >>${LOG_FILE} 2> # create and checkout new merge branch off of master if ! git checkout -b "${sub_project}/${branch_name}" master >>${LOG_FILE} 2> ; then failed "Failed preparing ${branch_name}" ; fi if ! git reset --hard ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2> ; fi if ! git clean -d --force ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2> ; fi # Merge the project echo -e "INFO: Merging ${sub_project}..." echo -e "INFO: Merging ${sub_project}..." >>${LOG_FILE} 2> if ! git merge --allow-unrelated-histories --no-commit "remotes/${sub_project}/${branch_name}" >>${LOG_FILE} 2> ; then failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/${branch_name}" # relocate projects files into own directory if [ "$(ls)" == "${sub_project}" ] ; then echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." >>${LOG_FILE} 2> else echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." >>${LOG_FILE} 2> mkdir ${sub_project} for f in $(ls -a) ; do if [[ "$f" == "${sub_project}" ]] || [[ "$f" == "." ]] || [[ "$f" == ".." ]] ; then continue fi git mv -k "$f" "${sub_project}/" done # commit the moving if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then failed "Failed to commit moving of ${sub_project} files into sub directory" fi fi echo done < <(git ls-remote --heads ${sub_project}) # checkout master of sub probject if ! git checkout "${sub_project}/master" >>${LOG_FILE} 2> ; then failed "sub_project ${sub_project} is missing master branch!" fi # copy remote tags echo -e "INFO: Copying tags for ${sub_project}..." echo -e "INFO: Copying tags for ${sub_project}..." >>${LOG_FILE} 2> while read tag ; do tag_ref=$(echo $tag | tr " " "\t" | cut -f 1) tag_name_unfixed=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3) # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0 tag_name="${tag_name_unfixed%%^*}" tag_new_name="${sub_project}/${tag_name}" echo -e "INFO: Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." echo -e "INFO: Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." >>${LOG_FILE} 2> if ! git tag "${tag_new_name}" "${tag_ref}" >>${LOG_FILE} 2> ; then echo -e "WARN: Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" echo -e "WARN: Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" >>${LOG_FILE} 2> fi done < <(git ls-remote --tags --refs ${sub_project}) # Remove the remote to the old project echo -e "INFO: Removing remote ${sub_project}..." echo -e "INFO: Removing remote ${sub_project}..." >>${LOG_FILE} 2> git remote rm ${sub_project} echo done # Now merge all project master branches into new master git checkout --quiet master echo -e "INFO: Merging projects master branches into new repository..." echo -e "INFO: Merging projects master branches into new repository..." >>${LOG_FILE} 2> echo -e "====================================================" echo -e "====================================================" >>${LOG_FILE} 2> for url in $(cat ${REPO_URL_FILE}) ; do if [[ ${url:0:1} == '#' ]] ; then continue fi # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Merging ${sub_project}..." echo -e "INFO: Merging ${sub_project}..." >>${LOG_FILE} 2> if ! git merge --allow-unrelated-histories --no-commit "${sub_project}/master" >>${LOG_FILE} 2> ; then failed "Failed to merge branch ${sub_project}/master into master" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/master" echo done # Done cd ${ROOT_DIR} echo -e "INFO: Done." echo -e "INFO: Done." >>${LOG_FILE} 2> echo exit 0 

Вы также можете получить его с http://paste.ubuntu.com/11732805