In meinem Fall hatte ich ein my-pluginRepository und ein main-projectRepository, und ich wollte so tun, als wäre my-plugindas immer im pluginsUnterverzeichnis von entwickelt worden main-project.
Grundsätzlich habe ich den Verlauf des my-pluginRepositorys so umgeschrieben , dass die gesamte Entwicklung im plugins/my-pluginUnterverzeichnis stattfand. Dann habe ich die Entwicklungsgeschichte von my-pluginin die main-projectGeschichte aufgenommen und die beiden Bäume zusammengeführt. Da war noch kein plugins/my-pluginVerzeichnis in dermain-project Repository , war dies eine triviale Zusammenführung ohne Konflikte. Das resultierende Repository enthielt den gesamten Verlauf beider Originalprojekte 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 Version
Erstellen Sie zunächst eine Kopie des my-pluginRepositorys, da wir den Verlauf dieses Repositorys neu schreiben werden.
Navigieren Sie nun zum Stammverzeichnis des my-pluginRepositorys, überprüfen Sie (wahrscheinlich master) Ihren Hauptzweig und führen Sie den folgenden Befehl aus. Natürlich sollten Sie ersetzen my-pluginund pluginswas auch immer Ihre tatsächlichen Namen sind.
$ 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
Nun zu einer Erklärung. git filter-branch --tree-filter (...) HEADführt den (...)Befehl für jedes Commit aus, von dem aus erreichbar ist HEAD. Beachten Sie, dass dies direkt mit den für jedes Commit gespeicherten Daten zusammenhängt, sodass wir uns nicht um die Begriffe "Arbeitsverzeichnis", "Index", "Staging" usw. kümmern müssen.
Wenn Sie einen filter-branchfehlgeschlagenen Befehl ausführen , bleiben einige Dateien im .gitVerzeichnis zurück, und beim nächsten Versuch filter-branchwird dies beanstandet, es sei denn, Sie geben die -fOption an filter-branch.
Was den eigentlichen Befehl angeht, hatte ich nicht viel Glück, das bashzu tun, was ich wollte, also verwende ich stattdessen zsh -c, zshum einen Befehl auszuführen. Zuerst setze ich die extended_globOption, die die ^(...)Syntax im mvBefehl glob_dotsaktiviert , sowie die Option, mit der ich Punktedateien (wie .gitignore) mit einem glob ( ^(...)) auswählen kann .
Als nächstes benutze ich den mkdir -pBefehl sowohl zu schaffen pluginsund plugins/my-pluginzugleich.
Schließlich verwende ich die Funktion zsh"Negative Glob" ^(.git|plugins), um alle Dateien im Stammverzeichnis des Repositorys mit Ausnahme .gitdes neu erstellten my-pluginOrdners abzugleichen. (Ein Ausschluss ist hier .gitmöglicherweise nicht erforderlich, aber der Versuch, ein Verzeichnis in sich selbst zu verschieben, ist ein Fehler.)
In meinem Repository enthielt das anfängliche Festschreiben keine Dateien, sodass der mvBefehl beim ersten Festschreiben einen Fehler zurückgab (da nichts zum Verschieben verfügbar war). Deshalb habe ich ein hinzugefügt || true, git filter-branchdamit nicht abgebrochen wird.
Die --allOption weist filter-branchan, den Verlauf für alle Zweige im Repository neu zu schreiben , und das Extra --muss angeben git, dass er als Teil der Optionsliste für neu zu schreibende Zweige und nicht als Option für sich filter-branchselbst interpretiert werden soll .
Navigieren Sie nun zu Ihrem main-projectRepository und überprüfen Sie den Zweig, in den Sie zusammenführen möchten. Fügen Sie Ihre lokale Kopie des my-pluginRepositorys (mit geändertem Verlauf) als Remote von hinzu main-project:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Sie haben jetzt zwei nicht verwandte Bäume in Ihrem Commit-Verlauf, die Sie mithilfe von:
$ git log --color --graph --decorate --all
Verwenden Sie zum Zusammenführen Folgendes:
$ git merge my-plugin/master --allow-unrelated-histories
Beachten Sie, dass in Git vor 2.9.0 die --allow-unrelated-historiesOption nicht vorhanden ist. Wenn Sie eine dieser Versionen verwenden, lassen Sie einfach die Option weg: Die Fehlermeldung, die dies --allow-unrelated-historiesverhindert, wurde auch in 2.9.0 hinzugefügt.
Sie sollten keine Zusammenführungskonflikte haben. Wenn Sie dies tun, bedeutet dies wahrscheinlich, dass entweder der filter-branchBefehl nicht ordnungsgemäß funktioniert hat oder bereits ein plugins/my-pluginVerzeichnis vorhanden war main-project.
Stellen Sie sicher, dass Sie eine erklärende Commit-Nachricht für zukünftige Mitwirkende eingeben, die sich fragen, was Hackery vor sich hat, um ein Repository mit zwei Wurzeln zu erstellen.
Mit dem obigen git logBefehl können Sie das neue Festschreibungsdiagramm visualisieren, das zwei Root-Festschreibungen enthalten sollte . Beachten Sie, dass nur der masterZweig zusammengeführt wird . Dies bedeutet, dass Sie, wenn Sie wichtige Arbeit an anderen my-pluginZweigen haben, die Sie in den main-projectBaum einbinden möchten, die my-pluginFernbedienung erst löschen sollten , wenn Sie diese Zusammenführungen durchgeführt haben. Wenn Sie dies nicht tun, befinden sich die Commits aus diesen Zweigen weiterhin im main-projectRepository, einige sind jedoch nicht erreichbar und können möglicherweise nicht ordnungsgemäß gespeichert werden. (Außerdem müssen Sie von SHA auf sie verweisen, da durch das Löschen einer Fernbedienung die Fernverfolgungszweige entfernt werden.)
Optional können Sie my-plugindie my-pluginFernbedienung entfernen , nachdem Sie alles zusammengeführt haben, was Sie behalten möchten :
$ git remote remove my-plugin
Sie können jetzt die Kopie des my-pluginRepositorys, dessen Verlauf Sie geändert haben, sicher löschen . In meinem Fall habe ich dem realen my-pluginRepository auch einen Verfallshinweis hinzugefügt, nachdem die Zusammenführung abgeschlossen und verschoben wurde.
Getestet unter Mac OS X El Capitan mit git --version 2.9.0und zsh --version 5.2. Ihr Kilometerstand kann variieren.
Verweise: