W moim przypadku miałem my-pluginrepozytorium i main-projectrepozytorium i chciałem udawać, że my-pluginzawsze było rozwijane w pluginspodkatalogu main-project.
Zasadniczo przepisałem historię my-pluginrepozytorium, aby wyglądało na to, że cały rozwój odbywał się w plugins/my-pluginpodkatalogu. Następnie dodałem historię rozwoju my-plugindo main-projecthistorii i połączyłem oba drzewa. Ponieważ plugins/my-pluginw main-projectrepozytorium 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
$ 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
Długa wersja
Najpierw utwórz kopię my-pluginrepozytorium, ponieważ zamierzamy przepisać historię tego repozytorium.
Teraz przejdź do katalogu głównego my-pluginrepozytorium, sprawdź swoją główną gałąź (prawdopodobnie master) i uruchom następującą komendę. Oczywiście powinieneś zastąpić my-plugini pluginsjakikolwiek jest twój rzeczywisty imion.
$ 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
Teraz wyjaśnienie. git filter-branch --tree-filter (...) HEADuruchamia (...)polecenie przy każdym dostępnym zatwierdzeniu HEAD. 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-branchpolecenie, które się nie powiedzie, pozostawi on niektóre pliki w .gitkatalogu, a przy następnej próbie filter-branchbędzie narzekał na to, chyba że podasz -fopcję filter-branch.
Jak dla rzeczywistego polecenia, nie mam dużo szczęścia z dostaniem bashsię do tego, co chciałem, więc zamiast tego użyć zsh -c, aby zshwykonać polecenie. Najpierw ustawiam extended_globopcję, która umożliwia ^(...)składnię mvpolecenia, a także glob_dotsopcję, która pozwala mi wybierać pliki dot (np. .gitignore) Za pomocą glob ( ^(...)).
Następnie używam mkdir -ppolecenia do tworzenia zarówno pluginsi plugins/my-pluginw tym samym czasie.
Na koniec używam funkcji zsh„negatywny glob”, ^(.git|plugins)aby dopasować wszystkie pliki w katalogu głównym repozytorium oprócz .giti nowo utworzonego my-pluginfolderu. (Wykluczenie .gitmoż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 mvpolecenie zwróciło błąd przy początkowym zatwierdzeniu (ponieważ nic nie było dostępne do przeniesienia). Dlatego dodałem || truetak, git filter-branchżeby się nie przerywało.
--allOpcja mówi filter-branchprzepisać historię dla wszystkich oddziałów w repozytorium, a dodatkowo --trzeba powiedzieć gitinterpretować go jako części listy opcji dla oddziałów przepisać, zamiast jako opcja do filter-branchsiebie.
Teraz przejdź do swojego main-projectrepozytorium i sprawdź gałąź, z którą chcesz się połączyć. Dodaj lokalną kopię my-pluginrepozytorium (ze zmodyfikowaną historią) jako zdalną main-projectz:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Będziesz teraz mieć dwa niezwiązane drzewa w swojej historii zmian, które możesz ładnie wizualizować za pomocą:
$ git log --color --graph --decorate --all
Aby je połączyć, użyj:
$ git merge my-plugin/master --allow-unrelated-histories
Zauważ, że w wersji wcześniejszej niż 2.9.0 --allow-unrelated-historiesopcja nie istnieje. Jeśli używasz jednej z tych wersji, po prostu pomiń tę opcję: komunikat o błędzie, który --allow-unrelated-historieszapobiega, 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-branchpolecenie nie działało poprawnie, albo w plugins/my-pluginkatalogu 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 logpolecenia możesz wizualizować nowy wykres zatwierdzania, który powinien mieć dwa główne zatwierdzenia . Pamiętaj, że tylko mastergałąź zostanie scalona . Oznacza to, że jeśli masz ważną pracę nad innymi my-plugingałęziami, które chcesz scalić z main-projectdrzewem, powinieneś powstrzymać się od usuwania my-pluginpilota, dopóki nie wykonasz tych scaleń. Jeśli tego nie zrobisz, zatwierdzenia z tych gałęzi nadal będą w main-projectrepozytorium, 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-pluginpilota za pomocą:
$ git remote remove my-plugin
Możesz teraz bezpiecznie usunąć kopię my-pluginrepozytorium, którego historię zmieniłeś. W moim przypadku dodałem również informację o wycofaniu do prawdziwego my-pluginrepozytorium po zakończeniu scalania i wypchnięciu.
Testowane na Mac OS X El Capitan z git --version 2.9.0i zsh --version 5.2. Twój przebieg może się różnić.
Bibliografia: