Rozważ następujący scenariusz:
Opracowałem mały eksperymentalny projekt A we własnym repozytorium Git. Teraz dojrzał i chciałbym, aby A był częścią większego projektu B, który ma swoje własne duże repozytorium. Chciałbym teraz dodać A jako podkatalog B.
Jak połączyć A w B, nie tracąc historii z żadnej strony?
git
merge
repository
git-subtree
static_rtti
źródło
źródło
Odpowiedzi:
Pojedynczą gałąź innego repozytorium można łatwo umieścić w podkatalogu zachowującym jego historię. Na przykład:
Pojawi się jako pojedynczy zatwierdzenie, w którym wszystkie pliki gałęzi głównej Rails są dodawane do katalogu „rails”. Jednak tytuł zatwierdzenia zawiera odniesienie do starego drzewa historii:
Gdzie
<rev>
jest skrót zatwierdzenia SHA-1. Nadal możesz zobaczyć historię, zrzucić winę na niektóre zmiany.Zauważ, że nie widzisz stąd prefiksu katalogu, ponieważ jest to faktycznie stara gałąź pozostawiona nietknięta. Powinieneś traktować to jak zwykły zatwierdzenie przeniesienia pliku: będziesz potrzebował dodatkowego skoku po osiągnięciu go.
Istnieją bardziej złożone rozwiązania, takie jak robienie tego ręcznie lub przepisywanie historii zgodnie z opisem w innych odpowiedziach.
Polecenie git-subtree jest częścią oficjalnego git-contrib, niektórzy menedżerowie pakietów instalują go domyślnie (OS X Homebrew). Ale może być konieczne samodzielne zainstalowanie go oprócz git.
źródło
git co v1.7.11.3
je... v1.8.3
).git log rails/somefile
nie wyświetla historii zatwierdzeń tego pliku, z wyjątkiem zatwierdzenia scalania. Jak sugerował @artfulrobot, sprawdź odpowiedź Grega Hewgilla . I może być konieczne użyciegit filter-branch
repozytorium, które chcesz uwzględnić.git subtree
może nie robić tego, co myślisz! Patrz tutaj dla bardziej kompletnego rozwiązania.Jeśli chcesz się połączyć
project-a
wproject-b
:Zaczerpnięty z: git scala różne repozytoria?
Ta metoda działała dla mnie całkiem dobrze, jest krótsza i moim zdaniem dużo czystsza.
Jeśli chcesz umieścić
project-a
w podkatalogu, możesz użyćgit-filter-repo
(filter-branch
jest odradzane ). Uruchom następujące polecenia przed powyższymi poleceniami:Przykład połączenia 2 dużych repozytoriów i umieszczenia jednego z nich w podkatalogu: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Uwaga:
--allow-unrelated-histories
parametr istnieje tylko od git> = 2.9. Zobacz Git - git merge Documentation / --allow-nonrelated-historiesAktualizacja : Dodano
--tags
zgodnie z sugestią @jstadler, aby zachować tagi.źródło
git mv source-dir/ dest/new-source-dir
git merge
krok kończy się niepowodzeniemfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
naprawia to, jak wyjaśniono w dokumentacji .--allow-unrelated-histories
został wprowadzony w git 2.9 . We wcześniejszych wersjach było to zachowanie domyślne.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Oto dwa możliwe rozwiązania:
Submoduły
Skopiuj repozytorium A do osobnego katalogu w większym projekcie B lub (może lepiej) sklonuj repozytorium A do podkatalogu w projekcie B. Następnie użyj podmoduła git, aby uczynić to repozytorium podmodułem repozytorium B.
Jest to dobre rozwiązanie dla luźno powiązanych repozytoriów, w których rozwój w repozytorium A trwa, a główną część rozwoju stanowi oddzielny samodzielny rozwój w A. Zobacz także strony SubmoduleSupport i GitSubmoduleTutorial na Git Wiki.
Scalanie poddrzewa
Możesz scalić repozytorium A z podkatalogiem projektu B, korzystając ze strategii scalania poddrzewa . Jest to opisane w Markcie Prinz w Subtree Merging and You .
(Opcja
--allow-unrelated-histories
jest wymagana dla Git> = 2.9.0.)Lub możesz użyć narzędzia git subtree ( repozytorium na GitHub ) autorstwa apenwarr (Avery Pennarun), ogłoszonego na przykład w jego blogu Nowa alternatywa dla submodułów Git: poddrzewo git .
Myślę, że w twoim przypadku (A ma być częścią większego projektu B) poprawnym rozwiązaniem byłoby użycie scalania poddrzewa .
źródło
git log dir-B/somefile
nie pokaże niczego oprócz jednego scalenia. Zobacz odpowiedź Grega Hewgilla odnosi się do tego ważnego problemu.Podmoduł jest dobry, jeśli chcesz zachować projekt osobno. Jeśli jednak naprawdę chcesz połączyć oba projekty w tym samym repozytorium, masz trochę więcej pracy do zrobienia.
Pierwszą rzeczą byłoby użyć
git filter-branch
do przepisania nazw wszystkiego w drugim repozytorium, aby znajdowały się w podkatalogu, w którym chciałbyś, aby się skończyły. Więc zamiastfoo.c
,bar.html
, to maszprojb/foo.c
iprojb/bar.html
.Następnie powinieneś być w stanie zrobić coś takiego:
git pull
Zrobigit fetch
następnie przezgit merge
. Nie powinno być żadnych konfliktów, jeśli repozytorium, do którego ciągniesz, nie ma jeszczeprojb/
katalogu.Dalsze poszukiwania wskazuje, że coś podobnego miało na celu scalenia
gitk
wgit
. Junio C Hamano pisze o tym tutaj: http://www.mail-archive.com/[email protected]/msg03395.htmlźródło
git filter-branch
tego dokonać. Na stronie podręcznika jest napisane o odwrotnej sytuacji: uczynienie subdir / rootem, ale nie na odwrót.git-subtree
jest fajny, ale prawdopodobnie nie jest to ten, którego chcesz.Na przykład jeśli
projectA
katalog jest utworzony w B, pogit subtree
,wyświetla tylko jedno zatwierdzenie: scalenie. Zatwierdzenia ze scalonego projektu są dla różnych ścieżek, więc się nie wyświetlają.
Odpowiedź Grega Hewgilla jest najbliższa, chociaż tak naprawdę nie mówi, jak przepisać ścieżki.
Rozwiązanie jest zaskakująco proste.
(1) w A
Uwaga: Ta historia przepisuje historię, więc jeśli zamierzasz nadal korzystać z tego repozytorium A, możesz najpierw sklonować (skopiować) jego wyrzuconą kopię.
Uwaga Bene: Musisz zmodyfikować skrypt zastępczy w komendzie sed, jeśli używasz znaków innych niż ascii (lub białych) w nazwach plików lub ścieżce. W takim przypadku lokalizacja pliku w rekordzie utworzonym przez „ls-files -s” zaczyna się od cudzysłowu.
(2) Następnie w B, uruchom
Voila! Masz
projectA
katalog w B. Jeśli uruchomiszgit log projectA
, zobaczysz wszystkie zatwierdzenia z A.W moim przypadku chciałem dwa podkatalogi
projectA
iprojectB
. W takim przypadku zrobiłem również krok (1) do B.źródło
"$GIT_INDEX_FILE"
musi być cytowany (dwa razy), w przeciwnym razie metoda zawiedzie, jeśli np. ścieżka zawiera spacje.Ctrl-V <tab>
Jeśli oba repozytoria mają ten sam rodzaj plików (jak dwa repozytoria Rails dla różnych projektów), możesz pobrać dane z repozytorium wtórnego do bieżącego repozytorium:
a następnie scal je z bieżącym repozytorium:
Jeśli Twoja wersja Git jest mniejsza niż 2.9, usuń
--allow-unrelated-histories
.Następnie mogą wystąpić konflikty. Możesz rozwiązać je na przykład za pomocą
git mergetool
.kdiff3
może być używany wyłącznie z klawiaturą, więc 5 konfliktów zajmuje tylko kilka minut po przeczytaniu kodu.Pamiętaj, aby zakończyć scalanie:
źródło
Ciągle traciłem historię, używając scalania, więc skończyło się na rebase, ponieważ w moim przypadku dwa repozytoria są na tyle różne, że nie łączą się przy każdym zatwierdzeniu:
=> rozwiąż konflikty, a następnie kontynuuj tyle razy, ile potrzeba ...
Powoduje to, że jeden projekt ma wszystkie zatwierdzenia z projA, a następnie zatwierdzenia z projB
źródło
W moim przypadku miałem
my-plugin
repozytorium imain-project
repozytorium i chciałem udawać, żemy-plugin
zawsze było rozwijane wplugins
podkatalogumain-project
.Zasadniczo przepisałem historię
my-plugin
repozytorium, aby wyglądało na to, że cały rozwój odbywał się wplugins/my-plugin
podkatalogu. Następnie dodałem historię rozwojumy-plugin
domain-project
historii i połączyłem oba drzewa. Ponieważplugins/my-plugin
wmain-project
repozytorium nie było już katalogu , było to trywialne połączenie bez konfliktów. Powstałe repozytorium zawierało całą historię obu oryginalnych projektów i miało dwa źródła.TL; DR
Długa wersja
Najpierw utwórz kopię
my-plugin
repozytorium, ponieważ zamierzamy przepisać historię tego repozytorium.Teraz przejdź do katalogu głównego
my-plugin
repozytorium, sprawdź swoją główną gałąź (prawdopodobniemaster
) i uruchom następującą komendę. Oczywiście powinieneś zastąpićmy-plugin
iplugins
jakikolwiek jest twój rzeczywisty imion.Teraz wyjaśnienie.
git filter-branch --tree-filter (...) HEAD
uruchamia(...)
polecenie przy każdym dostępnym zatwierdzeniuHEAD
. Zauważ, że działa to bezpośrednio na dane przechowywane dla każdego zatwierdzenia, więc nie musimy się martwić o pojęcia „katalogu roboczego”, „indeksu”, „przemieszczania” i tak dalej.Jeśli uruchomisz
filter-branch
polecenie, które się nie powiedzie, pozostawi on niektóre pliki w.git
katalogu, a przy następnej próbiefilter-branch
będzie narzekał na to, chyba że podasz-f
opcjęfilter-branch
.Jak dla rzeczywistego polecenia, nie mam dużo szczęścia z dostaniem
bash
się do tego, co chciałem, więc zamiast tego użyćzsh -c
, abyzsh
wykonać polecenie. Najpierw ustawiamextended_glob
opcję, która umożliwia^(...)
składnięmv
polecenia, a takżeglob_dots
opcję, która pozwala mi wybierać pliki dot (np..gitignore
) Za pomocą glob (^(...)
).Następnie używam
mkdir -p
polecenia do tworzenia zarównoplugins
iplugins/my-plugin
w tym samym czasie.Na koniec używam funkcji
zsh
„negatywny glob”,^(.git|plugins)
aby dopasować wszystkie pliki w katalogu głównym repozytorium oprócz.git
i nowo utworzonegomy-plugin
folderu. (Wykluczenie.git
może nie być tutaj konieczne, ale próba przeniesienia katalogu do siebie jest błędem).W moim repozytorium początkowe zatwierdzenie nie zawierało żadnych plików, więc
mv
polecenie zwróciło błąd przy początkowym zatwierdzeniu (ponieważ nic nie było dostępne do przeniesienia). Dlatego dodałem|| true
tak,git filter-branch
żeby się nie przerywało.--all
Opcja mówifilter-branch
przepisać historię dla wszystkich oddziałów w repozytorium, a dodatkowo--
trzeba powiedziećgit
interpretować go jako części listy opcji dla oddziałów przepisać, zamiast jako opcja dofilter-branch
siebie.Teraz przejdź do swojego
main-project
repozytorium i sprawdź gałąź, z którą chcesz się połączyć. Dodaj lokalną kopięmy-plugin
repozytorium (ze zmodyfikowaną historią) jako zdalnąmain-project
z:Będziesz teraz mieć dwa niezwiązane drzewa w swojej historii zmian, które możesz ładnie wizualizować za pomocą:
Aby je połączyć, użyj:
Zauważ, że w wersji wcześniejszej niż 2.9.0
--allow-unrelated-histories
opcja nie istnieje. Jeśli używasz jednej z tych wersji, po prostu pomiń tę opcję: komunikat o błędzie, który--allow-unrelated-histories
zapobiega, został również dodany w wersji 2.9.0.Nie powinieneś mieć żadnych konfliktów scalania. Jeśli to zrobisz, prawdopodobnie oznacza to, że albo
filter-branch
polecenie nie działało poprawnie, albo wplugins/my-plugin
katalogu już byłmain-project
.Upewnij się, że wprowadzasz wyjaśniający komunikat zatwierdzenia dla wszystkich przyszłych autorów, którzy zastanawiają się, co się dzieje, aby stworzyć repozytorium z dwoma źródłami.
Za pomocą powyższego
git log
polecenia możesz wizualizować nowy wykres zatwierdzania, który powinien mieć dwa główne zatwierdzenia . Pamiętaj, że tylkomaster
gałąź zostanie scalona . Oznacza to, że jeśli masz ważną pracę nad innymimy-plugin
gałęziami, które chcesz scalić zmain-project
drzewem, powinieneś powstrzymać się od usuwaniamy-plugin
pilota, dopóki nie wykonasz tych scaleń. Jeśli tego nie zrobisz, zatwierdzenia z tych gałęzi nadal będą wmain-project
repozytorium, ale niektóre będą nieosiągalne i podatne na ewentualne wyrzucanie elementów bezużytecznych. (Ponadto będziesz musiał odwoływać się do nich przez SHA, ponieważ usunięcie pilota usuwa jego gałęzie zdalnego śledzenia.)Opcjonalnie po scaleniu wszystkiego, co chcesz zachować
my-plugin
, możesz usunąćmy-plugin
pilota za pomocą:Możesz teraz bezpiecznie usunąć kopię
my-plugin
repozytorium, którego historię zmieniłeś. W moim przypadku dodałem również informację o wycofaniu do prawdziwegomy-plugin
repozytorium po zakończeniu scalania i wypchnięciu.Testowane na Mac OS X El Capitan z
git --version 2.9.0
izsh --version 5.2
. Twój przebieg może się różnić.Bibliografia:
źródło
--allow-unrelated-histories
pochodziszman git-merge
. Domyślnie polecenie git merge odmawia scalenia historii, które nie mają wspólnego przodka. Tej opcji można użyć, aby zastąpić to bezpieczeństwo podczas łączenia historii dwóch projektów, które rozpoczęły swoje życie niezależnie. Ponieważ jest to bardzo rzadka okazja, żadna zmienna konfiguracyjna domyślnie nie istnieje i nie zostanie dodana.git version 2.7.2.windows.1
?Próbowałem robić to samo od kilku dni, używam git 2.7.2. Pod poddrzewo nie zachowuje historii.
Możesz użyć tej metody, jeśli nie będziesz ponownie używać starego projektu.
Sugerowałbym, aby najpierw rozgałęzić B i pracować w oddziale.
Oto kroki bez rozgałęziania:
Jeśli teraz zalogujesz któryś z plików w podkatalogu A, otrzymasz pełną historię
Ten post pomógł mi to zrobić:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
źródło
Jeśli chcesz umieścić pliki z gałęzi w repozytorium B w poddrzewie repozytorium A, a także zachować historię, czytaj dalej. (W poniższym przykładzie zakładam, że chcemy scalić gałąź master repo B w gałąź master repo A.)
W repozytorium A wykonaj najpierw następujące czynności, aby udostępnić repozytorium B:
Teraz tworzymy nowy oddział (z tylko jednym zatwierdzeniem) w repozytorium A, które nazywamy
new_b_root
. Wynikowe zatwierdzenie będzie miało pliki, które zostały zatwierdzone w pierwszym zatwierdzeniu w gałęzi master repo B, ale umieszczone w podkatalogu o nazwiepath/to/b-files/
.Objaśnienie:
--orphan
Opcja polecenia checkout pobiera pliki z gałęzi master A, ale nie tworzy żadnego zatwierdzenia. Mogliśmy wybrać dowolny zatwierdzenie, ponieważ i tak usuwamy wszystkie pliki. Następnie, nie popełniając jeszcze (-n
), wybieramy pierwszy zatwierdzenie z gałęzi master B. (Cherry-pick zachowuje oryginalny komunikat zatwierdzenia, którego wydaje się, że nie wykonuje go proste pobranie). Następnie tworzymy poddrzewo, w którym chcemy umieścić wszystkie pliki z repozytorium B. Następnie musimy przenieść wszystkie pliki, które zostały wprowadzone w cherry-pick do poddrzewa. W powyższym przykładzie jest tylkoREADME
plik do przeniesienia. Następnie zatwierdzamy nasz główny zatwierdzenie B repo i jednocześnie zachowujemy znacznik czasu oryginalnego zatwierdzenia.Teraz utworzymy nowy
B/master
oddział na nowo utworzonymnew_b_root
. Nazywamy nowy oddziałb
:Teraz łączymy nasz
b
oddział wA/master
:Wreszcie możesz usunąć
B
gałęzie zdalne i tymczasowe:Ostateczny wykres będzie miał następującą strukturę:
źródło
Zebrałem tutaj wiele informacji na temat Stack OverFlow itp. I udało mi się stworzyć skrypt, który rozwiązuje problem.
Zastrzeżenie polega na tym, że bierze ono pod uwagę tylko gałąź „rozwijania” każdego repozytorium i łączy je w osobny katalog w całkowicie nowym repozytorium.
Tagi i inne gałęzie są ignorowane - to może nie być to, czego chcesz.
Skrypt obsługuje nawet gałęzie funkcji i tagi - zmieniając ich nazwy w nowym projekcie, abyś wiedział, skąd pochodzą.
Możesz go również uzyskać ze strony http://paste.ubuntu.com/11732805
Najpierw utwórz plik z adresem URL do każdego repozytorium, np .:
Następnie wywołaj skrypt podając nazwę projektu i ścieżkę do skryptu:
Sam skrypt ma wiele komentarzy, które powinny wyjaśniać, co robi.
źródło
Wiem, że minęło wiele czasu, ale nie byłem zadowolony z innych odpowiedzi, które tu znalazłem, więc napisałem:
źródło
if [[ $dirname =~ ^.*\.git$ ]]; then
Jeśli próbujesz po prostu skleić ze sobą dwa repozytoria, podmoduły i scalenia poddrzewa są niewłaściwym narzędziem do użycia, ponieważ nie zachowują całej historii plików (jak zauważyli inni w innych odpowiedziach). Zobacz tę odpowiedź tutaj, aby uzyskać prosty i poprawny sposób na zrobienie tego.
źródło
Podjąłem podobne wyzwanie, ale w moim przypadku opracowaliśmy jedną wersję bazy kodu w repozytorium A, a następnie sklonowaliśmy ją w nowej repozytorium, repo B, dla nowej wersji produktu. Po naprawieniu kilku błędów w repozytorium A, musieliśmy przesłać FI zmian w repozytorium B. Skończyło się na tym, że:
Pracowałem przysmak :)
źródło
Podobne do @Smar, ale korzysta ze ścieżek systemu plików, ustawionych w PODSTAWOWYM i WTÓRNYM:
Następnie ręcznie scalasz.
(dostosowano z postu Anar Manafov )
źródło
Scalanie 2 repozytoriów
źródło
Jeśli chcesz scalić trzy lub więcej projektów w jednym zatwierdzeniu, wykonaj kroki opisane w innych odpowiedziach (
remote add -f
,merge
). Następnie (miękki) zresetuj indeks do starej głowy (gdzie nie nastąpiło scalenie). Dodaj wszystkie pliki (git add -A
) i zatwierdź je (komunikat „Scalanie projektów A, B, C i D w jeden projekt). To jest teraz identyfikator zatwierdzenia master.Teraz utwórz
.git/info/grafts
z następującą zawartością:Uruchom
git filter-branch -- head^..head head^2..head head^3..head
. Jeśli masz więcej niż trzy gałęzie, po prostu dodaj tyle,head^n..head
ile masz gałęzi. Aby zaktualizować tagi, dołącz--tag-name-filter cat
. Nie zawsze dodawaj to, ponieważ może to spowodować przepisanie niektórych zmian. Aby uzyskać szczegółowe informacje, zobacz stronę man gałęzi filter- search, wyszukaj „przeszczepy”.Teraz do ostatniego zatwierdzenia przypisani są odpowiedni rodzice.
źródło
Aby scalić A w obrębie B:
1) W projekcie A
2) W projekcie B
W tej gałęzi wykonaj wszystkie operacje, które musisz wykonać, i zatwierdź je.
C) Następnie z powrotem do mistrza i klasyczne połączenie dwóch gałęzi:
źródło
Ta funkcja sklonuje zdalne repo do lokalnego repozytorium, po scaleniu wszystkie commity zostaną zapisane,
git log
pokaże oryginalne commity i odpowiednie ścieżki:Jak używać:
Jeśli wprowadzisz małe zmiany, możesz nawet przenieść pliki / katalogi scalonego repozytorium na różne ścieżki, na przykład:
Powiadomienia
Ścieżki zamieniają się przez
sed
, więc upewnij się, że po scaleniu poruszały się we właściwych ścieżkach.Ten
--allow-unrelated-histories
parametr istnieje tylko od git> = 2,9.źródło
Podane polecenie jest najlepszym możliwym rozwiązaniem, które sugeruję.
źródło
Scalam projekty lekko ręcznie, co pozwala mi uniknąć konieczności radzenia sobie z konfliktami scalania.
najpierw skopiuj pliki z innego projektu, tak jak chcesz.
następny ciąg historii
powiedz git, aby połączył się w historii ostatnio pobranej rzeczy
teraz zatwierdzaj, ale normalnie to robisz
źródło
Chciałem przenieść mały projekt do podkatalogu większego. Ponieważ mój mały projekt nie miał wielu zobowiązań, skorzystałem z niego
git format-patch --output-directory /path/to/patch-dir
. Potem wykorzystałem większy projektgit am --directory=dir/in/project /path/to/patch-dir/*
.Ten czuje sposób mniej przerażające i sposób bardziej czystsze niż w branży filtracyjnej. To prawda, że może nie mieć zastosowania do wszystkich przypadków.
źródło