Stellen Sie sich das folgende Szenario vor:
Ich habe ein kleines experimentelles Projekt A in einem eigenen Git-Repo entwickelt. Es ist jetzt gereift und ich möchte, dass A Teil des größeren Projekts B ist, das über ein eigenes großes Repository verfügt. Ich möchte jetzt A als Unterverzeichnis von B hinzufügen.
Wie verschmelze ich A mit B, ohne auf irgendeiner Seite die Geschichte zu verlieren?
git
merge
repository
git-subtree
static_rtti
quelle
quelle
Antworten:
Ein einzelner Zweig eines anderen Repositorys kann einfach unter ein Unterverzeichnis gestellt werden, wobei der Verlauf beibehalten wird. Zum Beispiel:
Dies wird als einzelnes Commit angezeigt, bei dem alle Dateien des Rails-Hauptzweigs zum Verzeichnis "Rails" hinzugefügt werden. Der Titel des Commits enthält jedoch einen Verweis auf den alten Verlaufsbaum:
Wo
<rev>
ist ein SHA-1-Commit-Hash? Sie können immer noch den Verlauf sehen, einige Änderungen beschuldigen.Beachten Sie, dass Sie das Verzeichnispräfix von hier aus nicht sehen können, da dies ein tatsächlicher alter Zweig ist, der intakt bleibt. Sie sollten dies wie ein übliches Commit zum Verschieben von Dateien behandeln: Sie benötigen einen zusätzlichen Sprung, wenn Sie es erreichen.
Es gibt komplexere Lösungen, wie dies manuell zu tun oder den Verlauf neu zu schreiben, wie in anderen Antworten beschrieben.
Der Befehl git-subtree ist Teil des offiziellen git-contrib. Einige Paketmanager installieren ihn standardmäßig (OS X Homebrew). Möglicherweise müssen Sie es jedoch zusätzlich zu git selbst installieren.
quelle
git co v1.7.11.3
mit... v1.8.3
).git log rails/somefile
der Commit-Verlauf dieser Datei mit Ausnahme des Merge-Commits nicht mehr angezeigt wird. Überprüfen Sie, wie @artfulrobot vorgeschlagen hat, die Antwort von Greg Hewgill . Möglicherweise müssen Siegit filter-branch
das Repo verwenden, das Sie einschließen möchten.git subtree
darf nicht tun, was Sie denken! Sehen Sie hier für eine vollständige Lösung.Wenn Sie zusammenführen möchten
project-a
inproject-b
:Entnommen aus: git verschiedene Repositories zusammenführen?
Diese Methode hat bei mir ziemlich gut funktioniert, sie ist kürzer und meiner Meinung nach viel sauberer.
Wenn Sie
project-a
in ein Unterverzeichnis einfügen möchten , können Sie verwendengit-filter-repo
(filter-branch
wird davon abgeraten ). Führen Sie die folgenden Befehle vor den obigen Befehlen aus:Ein Beispiel für das Zusammenführen von zwei großen Repositorys, wobei eines davon in einem Unterverzeichnis abgelegt wird: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Hinweis: Der
--allow-unrelated-histories
Parameter existiert nur seit git> = 2.9. Siehe Git - git merge Documentation / --allow-non-related-historiesUpdate :
--tags
Wie von @jstadler vorgeschlagen hinzugefügt, um Tags beizubehalten.quelle
git mv source-dir/ dest/new-source-dir
git merge
Schritt schlägt hier mit fehlfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
behebt das wie in den Dokumenten erklärt .--allow-unrelated-histories
wurde in git 2.9 eingeführt . In früheren Versionen war dies das Standardverhalten.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Hier sind zwei mögliche Lösungen:
Submodule
Kopieren Sie entweder Repository A in ein separates Verzeichnis in größerem Projekt B oder (vielleicht besser) klonen Sie Repository A in ein Unterverzeichnis in Projekt B. Verwenden Sie dann das Git-Submodul , um dieses Repository zu einem Submodul zu machen eines Repositorys B .
Dies ist eine gute Lösung für lose gekoppelte Repositorys, bei denen die Entwicklung in Repository A fortgesetzt wird und der größte Teil der Entwicklung eine separate eigenständige Entwicklung in A ist. Siehe auch SubmoduleSupport und GitSubmoduleTutorial Seiten im Git-Wiki.
Teilbaum zusammenführen
Sie können das Repository A mithilfe der Teilbaum-Zusammenführungsstrategie in ein Unterverzeichnis eines Projekts B zusammenführen . Dies wird in Subtree Merging and You von Markus Prinz beschrieben.
(Möglichkeit
--allow-unrelated-histories
wird für Git> = 2.9.0 benötigt.)Oder Sie können das Git-Teilbaum- Tool ( Repository auf GitHub ) von apenwarr (Avery Pennarun) verwenden, das beispielsweise in seinem Blog-Beitrag angekündigt wurde. Eine neue Alternative zu Git-Submodulen: Git-Teilbaum .
Ich denke, in Ihrem Fall (A soll Teil eines größeren Projekts B sein) wäre die richtige Lösung die Verwendung der Teilbaumzusammenführung .
quelle
git log dir-B/somefile
zeigt nichts außer der einen Zusammenführung. Siehe Greg Hewgills Antwort bezieht sich auf dieses wichtige Thema.Der Submodul-Ansatz ist gut, wenn Sie das Projekt separat verwalten möchten. Wenn Sie jedoch wirklich beide Projekte in demselben Repository zusammenführen möchten, müssen Sie etwas mehr Arbeit erledigen.
Das erste wäre,
git filter-branch
die Namen aller Elemente im zweiten Repository so umzuschreiben, dass sie sich im Unterverzeichnis befinden, in dem sie enden sollen. Anstatt alsofoo.c
,bar.html
würden Sie habenprojb/foo.c
undprojb/bar.html
.Dann sollten Sie in der Lage sein, Folgendes zu tun:
Das
git pull
wird eingit fetch
gefolgt von einem tungit merge
. Es sollte keine Konflikte geben, wenn das Repository, in das Sie ziehen, noch keine hatprojb/
Verzeichnis hat.Weitere Suche zeigt an, dass etwas Ähnliches getan wurde, um
gitk
sich zusammenzuschließengit
. Junio C Hamano schreibt hier darüber: http://www.mail-archive.com/[email protected]/msg03395.htmlquelle
git filter-branch
man dies erreicht. In der Manpage heißt es umgekehrt: Subdir / zur Wurzel machen, aber nicht umgekehrt.git-subtree
ist schön, aber es ist wahrscheinlich nicht das, was Sie wollen.Wenn zum Beispiel
projectA
das Verzeichnis , in B erstellt, nachgit subtree
,listet nur ein Commit auf: das Zusammenführen. Die Commits aus dem zusammengeführten Projekt gelten für verschiedene Pfade, sodass sie nicht angezeigt werden.
Greg Hewgills Antwort kommt am nächsten, obwohl sie eigentlich nicht sagt, wie man die Pfade umschreibt.
Die Lösung ist überraschend einfach.
(1) In A,
Hinweis: Dadurch wird der Verlauf neu geschrieben. Wenn Sie dieses Repo A weiterhin verwenden möchten, möchten Sie möglicherweise zuerst eine Wegwerfkopie davon klonen (kopieren).
Hinweis Vorteil: Sie müssen das Ersatzskript im Befehl sed ändern, wenn Sie in Dateinamen oder Pfaden Nicht-ASCII-Zeichen (oder weiße Zeichen) verwenden. In diesem Fall beginnt der Dateispeicherort in einem von "ls-files -s" erstellten Datensatz mit einem Anführungszeichen.
(2) Führen Sie dann in B aus
Voila! Sie haben ein
projectA
Verzeichnis in B. Wenn Sie ausführengit log projectA
, werden alle Commits von A angezeigt.In meinem Fall wollte ich zwei Unterverzeichnisse
projectA
undprojectB
. In diesem Fall habe ich auch Schritt (1) zu B gemacht.quelle
"$GIT_INDEX_FILE"
muss (zweimal) in Anführungszeichen gesetzt werden, sonst schlägt Ihre Methode fehl, wenn z. B. der Pfad Leerzeichen enthält.Ctrl-V <tab>
Wenn beide Repositorys dieselbe Art von Dateien haben (wie zwei Rails-Repositorys für verschiedene Projekte), können Sie Daten des sekundären Repositorys in Ihr aktuelles Repository abrufen:
und führen Sie es dann zum aktuellen Repository zusammen:
Wenn Ihre Git-Version kleiner als 2.9 ist, entfernen Sie
--allow-unrelated-histories
.Danach können Konflikte auftreten. Sie können sie beispielsweise mit auflösen
git mergetool
.kdiff3
kann nur mit Tastatur verwendet werden, so dass 5 Konfliktdateien beim Lesen des Codes nur wenige Minuten dauern.Denken Sie daran, die Zusammenführung zu beenden:
quelle
Ich habe bei der Verwendung von Merge immer wieder den Verlauf verloren, sodass ich letztendlich Rebase verwendet habe, da in meinem Fall die beiden Repositorys so unterschiedlich sind, dass sie nicht bei jedem Commit zusammengeführt werden:
=> Konflikte lösen, dann so oft wie nötig fortfahren ...
Dies führt dazu, dass ein Projekt alle Commits von projA enthält, gefolgt von Commits von projB
quelle
In meinem Fall hatte ich ein
my-plugin
Repository und einmain-project
Repository, und ich wollte so tun, als wäremy-plugin
das immer implugins
Unterverzeichnis von entwickelt wordenmain-project
.Grundsätzlich habe ich den Verlauf des
my-plugin
Repositorys so umgeschrieben , dass die gesamte Entwicklung implugins/my-plugin
Unterverzeichnis stattfand. Dann habe ich die Entwicklungsgeschichte vonmy-plugin
in diemain-project
Geschichte aufgenommen und die beiden Bäume zusammengeführt. Da war noch keinplugins/my-plugin
Verzeichnis 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
Lange Version
Erstellen Sie zunächst eine Kopie des
my-plugin
Repositorys, da wir den Verlauf dieses Repositorys neu schreiben werden.Navigieren Sie nun zum Stammverzeichnis des
my-plugin
Repositorys, überprüfen Sie (wahrscheinlichmaster
) Ihren Hauptzweig und führen Sie den folgenden Befehl aus. Natürlich sollten Sie ersetzenmy-plugin
undplugins
was auch immer Ihre tatsächlichen Namen sind.Nun zu einer Erklärung.
git filter-branch --tree-filter (...) HEAD
führt den(...)
Befehl für jedes Commit aus, von dem aus erreichbar istHEAD
. 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-branch
fehlgeschlagenen Befehl ausführen , bleiben einige Dateien im.git
Verzeichnis zurück, und beim nächsten Versuchfilter-branch
wird dies beanstandet, es sei denn, Sie geben die-f
Option anfilter-branch
.Was den eigentlichen Befehl angeht, hatte ich nicht viel Glück, das
bash
zu tun, was ich wollte, also verwende ich stattdessenzsh -c
,zsh
um einen Befehl auszuführen. Zuerst setze ich dieextended_glob
Option, die die^(...)
Syntax immv
Befehlglob_dots
aktiviert , sowie die Option, mit der ich Punktedateien (wie.gitignore
) mit einem glob (^(...)
) auswählen kann .Als nächstes benutze ich den
mkdir -p
Befehl sowohl zu schaffenplugins
undplugins/my-plugin
zugleich.Schließlich verwende ich die Funktion
zsh
"Negative Glob"^(.git|plugins)
, um alle Dateien im Stammverzeichnis des Repositorys mit Ausnahme.git
des neu erstelltenmy-plugin
Ordners abzugleichen. (Ein Ausschluss ist hier.git
mö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
mv
Befehl beim ersten Festschreiben einen Fehler zurückgab (da nichts zum Verschieben verfügbar war). Deshalb habe ich ein hinzugefügt|| true
,git filter-branch
damit nicht abgebrochen wird.Die
--all
Option weistfilter-branch
an, den Verlauf für alle Zweige im Repository neu zu schreiben , und das Extra--
muss angebengit
, dass er als Teil der Optionsliste für neu zu schreibende Zweige und nicht als Option für sichfilter-branch
selbst interpretiert werden soll .Navigieren Sie nun zu Ihrem
main-project
Repository und überprüfen Sie den Zweig, in den Sie zusammenführen möchten. Fügen Sie Ihre lokale Kopie desmy-plugin
Repositorys (mit geändertem Verlauf) als Remote von hinzumain-project
:Sie haben jetzt zwei nicht verwandte Bäume in Ihrem Commit-Verlauf, die Sie mithilfe von:
Verwenden Sie zum Zusammenführen Folgendes:
Beachten Sie, dass in Git vor 2.9.0 die
--allow-unrelated-histories
Option nicht vorhanden ist. Wenn Sie eine dieser Versionen verwenden, lassen Sie einfach die Option weg: Die Fehlermeldung, die dies--allow-unrelated-histories
verhindert, 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-branch
Befehl nicht ordnungsgemäß funktioniert hat oder bereits einplugins/my-plugin
Verzeichnis vorhanden warmain-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 log
Befehl können Sie das neue Festschreibungsdiagramm visualisieren, das zwei Root-Festschreibungen enthalten sollte . Beachten Sie, dass nur dermaster
Zweig zusammengeführt wird . Dies bedeutet, dass Sie, wenn Sie wichtige Arbeit an anderenmy-plugin
Zweigen haben, die Sie in denmain-project
Baum einbinden möchten, diemy-plugin
Fernbedienung 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 immain-project
Repository, 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-plugin
diemy-plugin
Fernbedienung entfernen , nachdem Sie alles zusammengeführt haben, was Sie behalten möchten :Sie können jetzt die Kopie des
my-plugin
Repositorys, dessen Verlauf Sie geändert haben, sicher löschen . In meinem Fall habe ich dem realenmy-plugin
Repository 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.0
undzsh --version 5.2
. Ihr Kilometerstand kann variieren.Verweise:
quelle
--allow-unrelated-histories
kommen sie?man git-merge
. Standardmäßig lehnt der Befehl git merge das Zusammenführen von Historien ab, die keinen gemeinsamen Vorfahren haben. Diese Option kann verwendet werden, um diese Sicherheit zu überschreiben, wenn Historien von zwei Projekten zusammengeführt werden, die ihr Leben unabhängig voneinander begonnen haben. Da dies sehr selten vorkommt, ist standardmäßig keine Konfigurationsvariable vorhanden, die dies aktiviert, und wird nicht hinzugefügt.git version 2.7.2.windows.1
?Ich habe seit Tagen versucht, das Gleiche zu tun, ich benutze Git 2.7.2. Teilbaum bewahrt die Geschichte nicht.
Sie können diese Methode verwenden, wenn Sie das alte Projekt nicht mehr verwenden.
Ich würde vorschlagen, dass Sie zuerst B verzweigen und in der Verzweigung arbeiten.
Hier sind die Schritte ohne Verzweigung:
Wenn Sie jetzt eine der Dateien in Unterverzeichnis A protokollieren, erhalten Sie den vollständigen Verlauf
Dies war der Beitrag, der mir dabei half:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
quelle
Wenn Sie die Dateien aus einem Zweig in Repo B in einen Teilbaum von Repo A einfügen und auch den Verlauf beibehalten möchten , lesen Sie weiter. (Im folgenden Beispiel gehe ich davon aus, dass der Hauptzweig von Repo B mit dem Hauptzweig von Repo A zusammengeführt werden soll.)
Führen Sie in Repo A zunächst die folgenden Schritte aus, um Repo B verfügbar zu machen:
Jetzt erstellen wir eine brandneue Filiale (mit nur einem Commit) in Repo A, die wir aufrufen
new_b_root
. Das resultierende Commit enthält die Dateien, die beim ersten Commit des Hauptzweigs von Repo B festgeschrieben wurden, aber in einem Unterverzeichnis namens aufgerufen wurdenpath/to/b-files/
.Erläuterung: Die
--orphan
Option zum Befehl checkout checkt die Dateien aus dem Hauptzweig von A aus, erstellt jedoch kein Commit. Wir hätten jedes Commit auswählen können, da wir als nächstes sowieso alle Dateien löschen. Dann, ohne noch ein-n
Commit ( ) durchzuführen, wählen wir das erste Commit aus dem Hauptzweig von B aus. (Der Cherry-Pick behält die ursprüngliche Festschreibungsnachricht bei, die ein direktes Auschecken nicht zu tun scheint.) Anschließend erstellen wir den Teilbaum, in den alle Dateien aus Repo B eingefügt werden sollen. Anschließend müssen alle Dateien verschoben werden, die in das Verzeichnis eingefügt wurden Kirschbaum zum Teilbaum. Im obigen Beispiel muss nur eineREADME
Datei verschoben werden. Dann schreiben wir unser B-Repo-Root-Commit fest und behalten gleichzeitig den Zeitstempel des ursprünglichen Commits bei.Jetzt erstellen wir einen neuen
B/master
Zweig über dem neu erstelltennew_b_root
. Wir nennen die neue Niederlassungb
:Jetzt verschmelzen wir unseren
b
Zweig zuA/master
:Schließlich können Sie die
B
entfernten und temporären Zweige entfernen :Das endgültige Diagramm hat eine Struktur wie folgt:
quelle
Ich habe hier viele Informationen über Stack OverFlow usw. gesammelt und es geschafft, ein Skript zusammenzustellen, das das Problem für mich löst.
Die Einschränkung besteht darin, dass nur der Zweig "Entwickeln" jedes Repositorys berücksichtigt und in einem separaten Verzeichnis in einem völlig neuen Repository zusammengeführt wird.
Tags und andere Zweige werden ignoriert - dies ist möglicherweise nicht das, was Sie wollen.
Das Skript verarbeitet sogar Feature-Zweige und -Tags und benennt sie im neuen Projekt um, damit Sie wissen, woher sie stammen.
Sie können es auch von http://paste.ubuntu.com/11732805 erhalten
Erstellen Sie zunächst eine Datei mit der URL zu jedem Repository, z.
Rufen Sie dann das Skript mit einem Namen des Projekts und dem Pfad zum Skript auf:
Das Skript selbst enthält viele Kommentare, die erklären sollten, was es tut.
quelle
Ich weiß, dass es lange nach der Tat ist, aber ich war mit den anderen Antworten, die ich hier gefunden habe, nicht zufrieden, also schrieb ich Folgendes:
quelle
if [[ $dirname =~ ^.*\.git$ ]]; then
Wenn Sie versuchen, einfach zwei Repositorys zusammenzukleben, sind Submodule und Zusammenführungen von Teilbäumen das falsche Werkzeug, da sie nicht den gesamten Dateiversionsverlauf beibehalten (wie in anderen Antworten angegeben). In dieser Antwort finden Sie die einfache und korrekte Vorgehensweise.
quelle
Ich hatte eine ähnliche Herausforderung, aber in meinem Fall hatten wir eine Version der Codebasis in Repo A entwickelt und diese dann in ein neues Repo, Repo B, für die neue Version des Produkts geklont. Nachdem wir einige Fehler in Repo A behoben hatten, mussten wir die Änderungen in Repo B korrigieren. Am Ende haben wir Folgendes getan:
Arbeitete ein Vergnügen :)
quelle
Ähnlich wie @Smar, verwendet jedoch Dateisystempfade, die in PRIMARY und SECONDARY festgelegt sind:
Dann führen Sie manuell zusammen.
(angepasst von Post von Anar Manafov )
quelle
Zusammenführen von 2 Repos
quelle
Wenn Sie drei oder mehr Projekte in einem einzigen Commit zusammenführen möchten , führen Sie die in den anderen Antworten (
remote add -f
,merge
) beschriebenen Schritte aus . Setzen Sie dann (weich) den Index auf den alten Kopf zurück (wo keine Zusammenführung stattgefunden hat). Fügen Sie alle Dateien hinzu (git add -A
) und schreiben Sie sie fest (Meldung "Projekte A, B, C und D zu einem Projekt zusammenführen). Dies ist jetzt die Festschreibungs-ID des Masters.Erstellen Sie nun
.git/info/grafts
mit folgendem Inhalt:Ausführen
git filter-branch -- head^..head head^2..head head^3..head
. Wenn Sie mehr als drei Zweige haben, fügenhead^n..head
Sie einfach so viel hinzu, wie Sie Zweige haben. Zum Aktualisieren von Tags anhängen--tag-name-filter cat
. Fügen Sie dies nicht immer hinzu, da dies zu einem Umschreiben einiger Commits führen kann. Einzelheiten finden Sie in der Manpage des Filterzweigs , suchen Sie nach "Transplantaten".Jetzt sind Ihrem letzten Commit die richtigen Eltern zugeordnet.
quelle
So fügen Sie ein A in B zusammen:
1) Im Projekt A.
2) Im Projekt B.
Führen Sie in diesem Zweig alle erforderlichen Vorgänge aus und legen Sie sie fest.
C) Dann zurück zum Meister und eine klassische Verschmelzung zwischen den beiden Zweigen:
quelle
Diese Funktion klont das Remote-Repo in das lokale Repo-Verzeichnis. Nach dem Zusammenführen werden alle Commits gespeichert und
git log
die ursprünglichen Commits und die richtigen Pfade angezeigt:Wie benutzt man:
Wenn Sie ein wenig Änderungen vornehmen, können Sie sogar Dateien / Verzeichnisse von zusammengeführtem Repo in verschiedene Pfade verschieben, zum Beispiel:
Hinweise
Pfade werden über ersetzt.
sed
Stellen Sie daher sicher, dass sie nach dem Zusammenführen in den richtigen Pfaden verschoben wurden.Der
--allow-unrelated-histories
Parameter existiert nur seit git> = 2.9.quelle
Ein gegebener Befehl ist die bestmögliche Lösung, die ich vorschlage.
quelle
Ich füge Projekte leicht manuell zusammen, wodurch ich vermeiden muss, mich mit Zusammenführungskonflikten befassen zu müssen.
Kopieren Sie zunächst die Dateien aus dem anderen Projekt, wie Sie möchten.
nächster Zug in der Geschichte
Sagen Sie git, er soll in der Geschichte des zuletzt abgerufenen Dings verschmelzen
Jetzt festschreiben, wie Sie es normalerweise tun würden
quelle
Ich wollte ein kleines Projekt in ein Unterverzeichnis eines größeren Projekts verschieben. Da mein kleines Projekt nicht viele Commits hatte, habe ich verwendet
git format-patch --output-directory /path/to/patch-dir
. Dann habe ich bei dem größeren Projekt verwendetgit am --directory=dir/in/project /path/to/patch-dir/*
.Das fühlt sich viel weniger beängstigend und viel sauberer an als ein Filterzweig. Zugegeben, es ist möglicherweise nicht in allen Fällen anwendbar.
quelle