2017-05-19 1 views
0

Ich habe ein Projekt, das mehr als 3 Jahre Geschichte im SVN-Repository hat. Es wurde auf Git umgestellt, aber der Typ, der das gemacht hat, nimmt einfach die letzte Version und wirft all diese 3 Jahre Geschichte raus.Ändern Sie den Root-Commit-Elternteil, um auf einen anderen Commit zu zeigen (Verbindung zweier unabhängiger Git-Repositories)

Jetzt hat das Projekt die letzten 3-4 Monate der Geschichte in einem Repository, und ich habe die anderen 3 Jahre SVN-Geschichte in ein neues Git-Repository importiert.

Gibt es eine Möglichkeit, den Root-Commit des zweiten Repository mit dem letzten Commit des ersten zu verbinden?

Es ist so etwas wie dieses:

* 2017-04-21 - last commit on master 
    | 
    * 2017-03-20 - merge branch Y into master 
    |\ 
    | * 2017-03-19 - commit on branch Y 
    | | 
    * | 2017-03-18 - merge branch X into master 
/| * 2017-02-17 - commit on another new branch Y 
* |/ 2017-02-16 - commit on branch X 
| * 2017-02-15 - commit on master branch 
* | 2017-01-14 - commit on new branch X 
\| 
    * 2017-01-13 - first commit on new repository 
    | 
    * 2017-01-12 - init new git project with the last version of the code in svn repository 
    . 
    . 
There is no relationship between the two different repositories yet, this is what I wanna 
do. I want to connect the root commit of 2nd repository with the last commit of the first 
one. 
    . 
    . 
    * 2017-01-09 - commit 
    | 
    * 2017-01-08 - commit 
    | 
    * 2017-01-07 - merge 
/| 
* | 2016-01-06 - 2nd commit the other branch 
| * 2016-01-05 - commit on trunk 
* | 2016-01-04 - commit on new branch 
\| 
    * 2015-01-03 - first commit 
    | 
    * 2015-01-02 - beggining of the project 

Update:

Ich lerne gerade, dass ich eine git rebase tun müssen, aber wie? Bitte, lassen Sie uns die Commit-Daten betrachten, wie es die SHA-1-Codes waren ... Die Antwort war, git filter-branch mit --parent-filter Option zu verwenden, kein git rebase.

Update 2:

ich den Befehl versucht git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD und es hat nicht funktioniert:

PS D:\git\rebase-test\rep2cc> git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD 
fatal: ambiguous argument '98e2b95e07b84ad1e40c3231e66840ea910e9d66 || cat': unknown revision or path not in the working tree. 
Use '--' to separate paths from revisions, like this: 
'git <command> [<revision>...] -- [<file>...]' 

Update 3:

Es hat nicht auf Windows CMD arbeiten oder PowerShell, aber es hat in Git Bash unter Windows funktioniert.

+2

Nun, haben Sie darüber nachgedacht, beide im selben Repository zu holen und dann eine Geschichte auf eine andere zu übertragen?Dies wird natürlich alle Commits der Geschichte umschreiben, die du neu lädst. –

+1

Lasse hat es richtig gemacht. Richte einfach einen neuen Repo mit korrekt geklonten svn ein, füge eine Fernbedienung mit diesem georteten Repo hinzu, hole und pflücke in den korrekt geklonten svn Repo den Verlauf, der nach dem Klonen von svn auf Git ausgeführt wurde. – eftshift0

+0

Ich bin neu auf Git, ich lerne gerade, dass das Zauberwort, das ich suchte, ist "Rebase" – lmcarreiro

Antwort

2

Das Wichtigste zuerst: Sie benötigen ein einzelnes Repo mit der gesamten verfügbaren Historie.

Machen Sie einen Klon des Repos mit der jüngsten Geschichte. Fügen Sie den Repo mit dem alten Verlauf als Remote hinzu. Ich empfehle, dass dieser Klon ein "Spiegel" ist und dass Sie damit aufhören, Ihr Ursprungs-Repo durch dieses zu ersetzen. Aber alternativ können Sie --mirror ausschalten, und Sie werden durch Drücken (möglicherweise zwingen drücken, je nachdem, welchen Ansatz Sie verwenden) alle zurück zum Ursprung verweist.

git clone --mirror url/of/current/repo 
cd repo 
git remote add history url/of/historical/repo 
git fetch history 

Das nächste, was Sie tun müssen, ist herauszufinden, wo Sie die Geschichte spleißen werden. Die Terminologie, um dies zu beschreiben, ist ein bisschen verschwommen. Ich denke ... was Sie wollen, ist, die zwei Commits zu finden, die der letzten SVN-Revision entsprechen, für die beide Historien einen Commit haben. Zum Beispiel enthalten Ihre SVN-Repo-Versionen 1, 2, 3 und 4. Jetzt haben Sie

Recent-History Repo 

C --- D --- E --- F <--(master) 

Old-History Repo 

A --- B --- C' --- D' 

wo A Version 1 darstellt, B Version 2, C und C' repräsentieren Version 3 und D und D' Version darstellen 4. E und F werden nach der ursprünglichen Migration erstellt. Sie möchten also die Commits, deren Vater (E in diesem Beispiel) ist, auf D' spleißen.

Jetzt kann ich an zwei Ansätze denken, jeder mit Vor- und Nachteilen.

Umschreiben der jüngsten Geschichte

IMO die beste Art und Weise , wenn Sie einen Ausschnitt über alle Entwickler zu einem neuen Repo koordinieren können (dh Sie können eine Zeit arrangieren, wenn sie alle einig, dass alle ausstehenden Arbeiten gedrängt, so dass sie ihre Klone verwerfen; dann machst du die Umwandlung; dann werden sie alle neu kloniert), um den letzten Verlauf (effektiv) auf den alten Verlauf zu rebasieren.

Wenn es wirklich nur eine einzige Zweigstelle ist, dann kann man buchstäblich rebase verwendet

git rebase --onto D' D master 

(wo D und D' mit der SHA-ID des Commits ersetzt).

Wahrscheinlich haben Sie einige Zweige und Zusammenführungen in der jüngeren Geschichte; In diesem Fall wird ein Rebase-Vorgang sehr schnell zum Problem werden. Auf der anderen Seite können Sie die Tatsache ausnutzen, dass den gleichen Baum wie D' hat - also sind ein Rebase und ein Re-Parent mehr oder weniger gleichwertig.

So können Sie git filter-branch mit einem --parent-filter verwenden, um das Neuschreiben zu tun. Basierend auf den Beispielen in der Dokumentation zu https://git-scm.com/docs/git-filter-branch würden Sie so etwas wie

git filter-branch --parent-filter 'test $GIT_COMMIT = D && echo "-p D'" || cat' HEAD 

tun (wo wieder D und D' mit der SHA-ID des Commits ersetzt).

Dies erstellt "Backup" -Referenzen, die Sie bereinigen müssen. Am Ende werden Sie

A --- B --- C' --- D' --- E' --- F' <--(master) 

Es ist die Tatsache erhalten, dass F durch F' ersetzen war, die die Notwendigkeit für eine forcierte über erzeugt (mehr oder weniger).

Wenn Sie nun in Schritt 1 einen Spiegelklon erstellt haben, können Sie erwägen, den Reflog zu löschen, die Remotes zu löschen und gc auszuführen. Dann handelt es sich um ein neues, ready-to-use-Ursprungsrepo.

Wenn Sie einen normalen Klon erstellt haben, dann müssen Sie push -f alle Referenzen auf den Ursprung, und dies wird wahrscheinlich einige Unordnung auf dem Ursprung Repo hinterlassen.

eine Verwendung von „Ersatz verpflichten“

Die andere Option keinen harten Schnitt-over schafft, aber es lässt Sie mit kleinen Kopfschmerzen mit immer beschäftigen. Sie können git replace verwenden. In Ihrem kombinierten Repo

git replace `D` `D'` 

standardmäßig, wenn Protokollausgabe oder was auch immer zu erzeugen, wenn git D findet, wird es D' (und seine Geschichte) in der Ausgabe ersetzen.

Es gibt einige bekannte Störungen. Es kann unbekannte Störungen geben. Und standardmäßig werden die "Ersatzreferenzen", die das alles funktionieren lassen, nicht geteilt, so dass du sie absichtlich pushen und abholen musst.

+0

Ich habe versucht den Git ersetzen, es ist nicht was ich will. Und ich habe viele Verzweigungen und Zusammenführungen in der Geschichte, also wurde die Rebase-Operation ein Problem, wie du gesagt hast. – lmcarreiro

+0

Diese dritte Option verstehe ich nicht ... Was ist 'P' und' P''? Und der Befehl 'git filter-branch --parent-filter 'test $ GIT_COMMIT = D && echo" -p D' "|| Katze 'HEAD' wird es auf Windows funktionieren? – lmcarreiro

+0

Sorry, "' P' und 'P''" hätten wie sonst wo 'D' und' D'' sein sollen; Ich habe aktualisiert. Der Befehl funktioniert mit Windows in einer 'git bash' Shell; Denken Sie daran, 'D' und' D''durch geeignete Commit-Referenzen (z. B. SHA-IDs) zu ersetzen. –

Verwandte Themen