In meinem Fall hatte ich eine my-plugin
main-project
Repository, und ich wollte so tun, als ob my-plugin
war immer in der Region entwickelt worden. plugins
Unterverzeichnis von main-project
.
Im Grunde habe ich die Geschichte der my-plugin
Repository, so dass es den Anschein hat, dass die gesamte Entwicklung im plugins/my-plugin
Unterverzeichnis. Dann fügte ich die Entwicklungsgeschichte von my-plugin
in die main-project
Geschichte und führte die beiden Bäume zusammen. Da es keine plugins/my-plugin
Verzeichnis, das bereits in der main-project
Repository war dies ein trivialer, konfliktfreier Zusammenschluss. Das resultierende Repository enthielt die gesamte Historie der beiden ursprünglichen Projekte und hatte zwei Wurzeln.
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
Lange Fassung
Erstellen Sie zunächst eine Kopie der Datei my-plugin
Repository, weil wir die Geschichte dieses Repositorys neu schreiben werden.
Navigieren Sie nun zur Wurzel des Dokuments my-plugin
Repository, checken Sie Ihren Hauptzweig aus (wahrscheinlich master
), und führen Sie den folgenden Befehl aus. Natürlich sollten Sie für my-plugin
et plugins
wie auch immer ihr eigentlich heißt.
$ 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
Und nun eine Erklärung. git filter-branch --tree-filter (...) HEAD
läuft die (...)
auf jeder Übertragung, die von HEAD
. Beachten Sie, dass dies direkt mit den für jede Übertragung gespeicherten Daten arbeitet, so dass wir uns keine Gedanken über Begriffe wie "Arbeitsverzeichnis", "Index", "Staging" usw. machen müssen.
Wenn Sie eine filter-branch
Befehl fehlschlägt, werden einige Dateien in der Datei .git
Verzeichnis und beim nächsten Mal, wenn Sie versuchen filter-branch
wird es sich darüber beschweren, es sei denn, Sie geben die -f
Option zu filter-branch
.
Was den eigentlichen Befehl anbelangt, so hatte ich nicht viel Glück bei der bash
um das zu tun, was ich wollte, also benutze ich stattdessen zsh -c
zu machen zsh
einen Befehl ausführen. Zuerst setze ich die extended_glob
die es ermöglicht, die Option ^(...)
Syntax in der mv
sowie der Befehl glob_dots
die es mir ermöglicht, Punktdateien auszuwählen (z. B. .gitignore
) mit einem Glob ( ^(...)
).
Als nächstes verwende ich die mkdir -p
Befehl, um beides zu erstellen plugins
et plugins/my-plugin
zur gleichen Zeit.
Schließlich verwende ich die zsh
"Merkmal "negativer Globus ^(.git|plugins)
um alle Dateien im Root-Verzeichnis des Repositorys abzugleichen, außer für .git
und die neu erstellte my-plugin
Ordner. (Ausgenommen .git
ist hier vielleicht nicht notwendig, aber der Versuch, ein Verzeichnis in sich selbst zu verschieben, ist ein Fehler).
In meinem Repository enthielt die erste Übertragung keine Dateien, so dass die mv
einen Fehler bei der ersten Übertragung zurück (da nichts zum Verschieben verfügbar war). Daher fügte ich eine || true
so dass git filter-branch
nicht abbrechen würde.
El --all
Option sagt filter-branch
die Geschichte neu zu schreiben für todo Zweige im Repository und die zusätzlichen --
ist notwendig, um zu sagen git
als Teil der Optionsliste für neu zu schreibende Zweige zu interpretieren, anstatt als Option für filter-branch
selbst.
Navigieren Sie nun zu Ihrem main-project
Repository und checken Sie den Zweig aus, in den Sie zusammenführen wollen. Fügen Sie Ihre lokale Kopie der my-plugin
Repository (mit geänderter Historie) als Remote von main-project
mit:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Sie werden nun zwei nicht miteinander verbundene Bäume in Ihrem Commit-Verlauf haben, die Sie gut visualisieren können:
$ git log --color --graph --decorate --all
Um sie zusammenzuführen, verwenden Sie:
$ git merge my-plugin/master --allow-unrelated-histories
Beachten Sie, dass in Git vor Version 2.9.0 die --allow-unrelated-histories
gibt es nicht. Wenn Sie eine dieser Versionen verwenden, lassen Sie die Option einfach weg: Die Fehlermeldung, die --allow-unrelated-histories
verhindert wurde auch hinzugefügt in 2.9.0.
Sie sollten keine Konflikte beim Zusammenführen haben. Falls doch, bedeutet dies wahrscheinlich, dass entweder die filter-branch
Befehl nicht richtig funktioniert hat oder bereits ein plugins/my-plugin
Verzeichnis in main-project
.
Vergewissern Sie sich, dass Sie eine erklärende Commit-Nachricht für alle zukünftigen Mitwirkenden eingeben, die sich fragen, was für ein Hickhack passiert ist, um ein Repository mit zwei Wurzeln zu erstellen.
Sie können den neuen Commit-Graphen, der zwei Root-Commits haben sollte, mit der obigen Grafik visualisieren git log
Befehl. Beachten Sie, dass nur die master
Der Zweig wird zusammengeführt . Das bedeutet, dass Sie, wenn Sie wichtige Arbeiten an anderen my-plugin
Zweige, die Sie in die main-project
Baum, sollten Sie davon absehen, die my-plugin
entfernt, bis Sie diese Zusammenführungen durchgeführt haben. Wenn Sie das nicht tun, werden die Commits aus diesen Zweigen immer noch in der main-project
Repository, aber einige werden unerreichbar und anfällig für eine eventuelle Garbage Collection sein. (Außerdem müssen Sie auf sie mit SHA verweisen, da das Löschen eines entfernten Objekts dessen Remote-Tracking-Zweige entfernt).
Optional können Sie, nachdem Sie alles zusammengeführt haben, was Sie von my-plugin
entfernen, können Sie die my-plugin
fernbedienen:
$ git remote remove my-plugin
Sie können nun die Kopie der Datei my-plugin
Repository, dessen Geschichte Sie verändert haben. In meinem Fall fügte ich auch einen Hinweis auf die Verwerfung der echten my-plugin
Repository, nachdem die Zusammenführung abgeschlossen und veröffentlicht wurde.
Getestet auf Mac OS X El Capitan mit git --version 2.9.0
et zsh --version 5.2
. Ihre Erfahrungen können variieren.
Referenzen:
17 Stimmen
Wenn Sie nur versuchen, zwei Repositories zu einem einzigen zusammenzufassen, ohne beide Repositories behalten zu müssen, werfen Sie einen Blick auf diese Frage: stackoverflow.com/questions/13040958/
0 Stimmen
Für das Zusammenführen von Git Repo in einem benutzerdefinierten Verzeichnis mit Speicherung aller Comits verwenden Sie stackoverflow.com/a/43340714/1772410