Brechen Sie einen vorherigen Commit in mehrere Commits

Ohne einen Zweig zu erstellen und eine Menge flippiger Arbeit an einem neuen Zweig zu erledigen, ist es möglich, einen einzelnen Commit in ein paar verschiedene Commits zu zerlegen, nachdem er an das lokale Repository übergeben wurde?

git rebase -i wird es tun.

Beginnen Sie zunächst mit einem sauberen Arbeitsverzeichnis: git status sollte keine ausstehenden Änderungen, Löschungen oder Ergänzungen anzeigen.

Um Ihr letztes Commit zu teilen, zuerst:

 $ git reset HEAD~ 

Verpflichten Sie nun die Teile einzeln auf die übliche Art und Weise, so viele Commits wie Sie benötigen.

Wenn es weiter hinten im Baum war, dann

 $ git rebase -i HEAD~3 

wo 3 ist, wie viele commits zurück es ist.

Wenn es weiter hinten im Baum war, als Sie zählen wollen, dann

 $ git rebase -i 123abcd~ 

Dabei ist 123abcd die SHA1 des Commits, das Sie aufteilen möchten.

Wenn Sie den Rebase-Bearbeitungsbildschirm sehen, suchen Sie nach dem Commit, das Sie trennen möchten. Am Anfang dieser Zeile ersetzen Sie die pick durch edit ( e für kurz). Speichere den Puffer und verlasse ihn. Rebase stoppt nun direkt nach dem Commit, das Sie bearbeiten möchten. Dann:

 $ git reset HEAD~ 

Setze die Teile einzeln auf die übliche Weise zusammen und erzeuge so viele Commits, wie du brauchst

 $ git rebase --continue 

Im git-rebase- Handbuch (Abschnitt SPLITTING COMMITS)

Im interaktiven Modus können Sie Commits mit der Aktion “Bearbeiten” markieren. Dies bedeutet jedoch nicht unbedingt, dass git rebase erwartet, dass das Ergebnis dieser Bearbeitung genau ein Commit ist. In der Tat können Sie das Commit rückgängig machen oder andere Commits hinzufügen. Dies kann verwendet werden, um ein Commit in zwei Teile aufzuteilen:

  • Starten Sie ein interaktives Rebase mit git rebase -i ^ , wobei das Commit ist, das Sie teilen möchten. In der Tat wird jeder Commit-Bereich ausreichen, solange er diesen Commit enthält.

  • Markieren Sie das Commit, das Sie teilen möchten, mit der Aktion “Bearbeiten”.

  • Wenn Sie diesen Commit bearbeiten, führen Sie git reset HEAD^ . Der Effekt ist, dass der HEAD um eins zurückgespult wird und der Index folgt. Der Arbeitsbaum bleibt jedoch gleich.

  • Fügen Sie nun die Änderungen dem Index hinzu, den Sie beim ersten Commit haben möchten. Sie können git add (möglicherweise interaktiv) oder git gui (oder beides) verwenden, um das zu tun.

  • Übergeben Sie den jetzt aktuellen Index mit der aktuellen Commit-Nachricht.

  • Wiederholen Sie die letzten beiden Schritte, bis Ihr Arbeitsbaum sauber ist.

  • Setzen Sie die Rebase mit git rebase --continue .

Benutze git rebase --interactive , um diesen früheren Commit zu bearbeiten, git rebase --interactive git reset HEAD~ , und dann git add -p , um einige hinzuzufügen, dann führe einen Commit durch, füge dann einige hinzu und git rebase --interactive wie git rebase --interactive . Wenn du fertig bist, git rebase --continue , und du hast alle Split-Commits früher in deinem Stack.

Wichtig : Beachten Sie, dass Sie git reflog und alle gewünschten Änderungen vornehmen können und sich keine Sorgen machen müssen, alte Änderungen zu verlieren, da Sie git reflog immer ausführen git reflog , um den Punkt in Ihrem Projekt zu finden, der die gewünschten Änderungen enthält (rufen wir an) es a8c4ab ), und dann git reset a8c4ab .

Hier ist eine Reihe von Befehlen, die zeigen, wie es funktioniert:

mkdir git-test; cd git-test; git init

Jetzt füge eine Datei hinzu A

vi A

füge diese Zeile hinzu:

one

git commit -am one

Dann füge diese Zeile zu A hinzu:

two

git commit -am two

Dann füge diese Zeile zu A hinzu:

three

git commit -am three

Jetzt sieht die Datei A wie folgt aus:

 one two three 

und unser git log sieht wie folgt aus (gut, ich benutze git log --pretty=oneline --pretty="%h %cn %cr ---- %s"

 bfb8e46 Rose Perrone 4 seconds ago ---- three 2b613bc Rose Perrone 14 seconds ago ---- two 9aac58f Rose Perrone 24 seconds ago ---- one 

Nehmen wir an, wir wollen das zweite Commit teilen, two .

git rebase --interactive HEAD~2

Dies bringt eine Nachricht, die wie folgt aussieht:

 pick 2b613bc two pick bfb8e46 three 

Ändern Sie die erste pick in ein e , um das Commit zu bearbeiten.

git reset HEAD~

git diff zeigt uns, dass wir gerade das Commit, das wir für das zweite Commit gemacht haben, inszeniert haben:

 diff --git a/A b/A index 5626abf..814f4a4 100644 --- a/A +++ b/A @@ -1 +1,2 @@ one +two 

Lassen Sie uns diese Änderung inszenieren und fügen Sie dieser Zeile in Datei A “und ein drittes” hinzu.

git add .

Dies ist normalerweise der Punkt während einer interaktiven Rebase, in der wir git rebase --continue würden git rebase --continue , weil wir normalerweise nur in unseren Commit-Stapel zurückgehen wollen, um einen früheren Commit zu bearbeiten. Aber dieses Mal wollen wir ein neues Commit erstellen. Also werden wir git commit -am 'two and a third' ausführen. Jetzt bearbeiten wir Datei A und fügen die Zeile two and two thirds .

git add . git commit -am 'two and two thirds' git rebase --continue

Wir haben einen Konflikt mit unserem Commit, three , also lass es uns lösen:

Wir werden uns ändern

 one < <<<<<< HEAD two and a third two and two thirds ======= two three >>>>>>> bfb8e46... three 

zu

 one two and a third two and two thirds three 

git add .; git rebase --continue

Nun sieht unser git log -p aus:

 commit e59ca35bae8360439823d66d459238779e5b4892 Author: Rose Perrone  Date: Sun Jul 7 13:57:00 2013 -0700 three diff --git a/A b/A index 5aef867..dd8fb63 100644 --- a/A +++ b/A @@ -1,3 +1,4 @@ one two and a third two and two thirds +three commit 4a283ba9bf83ef664541b467acdd0bb4d770ab8e Author: Rose Perrone  Date: Sun Jul 7 14:07:07 2013 -0700 two and two thirds diff --git a/A b/A index 575010a..5aef867 100644 --- a/A +++ b/A @@ -1,2 +1,3 @@ one two and a third +two and two thirds commit 704d323ca1bc7c45ed8b1714d924adcdc83dfa44 Author: Rose Perrone  Date: Sun Jul 7 14:06:40 2013 -0700 two and a third diff --git a/A b/A index 5626abf..575010a 100644 --- a/A +++ b/A @@ -1 +1,2 @@ one +two and a third commit 9aac58f3893488ec643fecab3c85f5a2f481586f Author: Rose Perrone  Date: Sun Jul 7 13:56:40 2013 -0700 one diff --git a/A b/A new file mode 100644 index 0000000..5626abf --- /dev/null +++ b/A @@ -0,0 +1 @@ +one 

In früheren Antworten wurde die Verwendung von git rebase -i , um das Commit zu bearbeiten, das Sie teilen möchten, und es in Teilen zu übergeben.

Dies funktioniert gut, wenn Sie die Dateien in verschiedene Commits aufteilen. Wenn Sie jedoch Änderungen an den einzelnen Dateien aufteilen möchten, müssen Sie mehr wissen.

Wenn Sie zu der rebase -i , die Sie teilen möchten, verwenden Sie die rebase -i und markieren Sie sie zur edit . Sie haben zwei Möglichkeiten.

  1. Nachdem Sie git reset HEAD~ , gehen Sie einzeln mit git add -p durch die Patches, um die gewünschten Patches auszuwählen

  2. Bearbeiten Sie die Arbeitskopie, um die Änderungen zu entfernen, die Sie nicht möchten. begehen diesen Zwischenzustand; und dann ziehe das volle Commit für die nächste Runde zurück.

Option 2 ist nützlich, wenn Sie ein großes Commit aufteilen, da Sie überprüfen können, ob die vorläufigen Versionen als Teil der Zusammenführung ordnungsgemäß erstellt und ausgeführt werden. Dies läuft wie folgt ab.

Verwenden rebase -i nach der Verwendung von rebase -i und edit des Commits

 git reset --soft HEAD~ 

um die Festschreibung rückgängig zu machen, aber die festgeschriebenen Dateien im Index belassen. Sie können auch einen gemischten Reset durchführen, indem Sie –soft auslassen, je nachdem, wie nahe das Endergebnis Ihrer ursprünglichen Festschreibung ist. Der einzige Unterschied besteht darin, ob Sie mit allen vorgenommenen Änderungen beginnen oder mit ihnen alle nicht verschoben sind.

Gehe jetzt rein und bearbeite den Code. Sie können Änderungen entfernen, hinzugefügte Dateien löschen und alles tun, was Sie wollen, um das erste Commit der gesuchten Serie zu erstellen. Sie können es auch erstellen, ausführen und bestätigen, dass Sie über eine konsistente Quelle verfügen.

Sobald Sie zufrieden sind, können Sie die Dateien nach Bedarf staged / unstagieren (ich verwende gerne git gui dafür) und die Änderungen über die Benutzeroberfläche oder die Befehlszeile festschreiben

 git commit 

Das ist der erste Einsatz, der gemacht wurde. Jetzt möchten Sie Ihre Arbeitskopie in den Zustand zurückversetzen, den sie nach dem Festschreiben hatten, das Sie teilen, damit Sie mehr von den Änderungen für Ihr nächstes Festschreiben übernehmen können. Verwenden Sie git status um den sha1 des zu bearbeitenden Commits zu finden. In den ersten Zeilen des Status sehen Sie den Rebase-Befehl, der gerade ausgeführt wird und in dem Sie das sha1 Ihres ursprünglichen Commits finden können:

 $ git status interactive rebase in progress; onto be83b41 Last commands done (3 commands done): pick 4847406 US135756: add debugging to the file download code e 65dfb6a US135756: write data and download from remote (see more in file .git/rebase-merge/done) ... 

In diesem Fall hat der Commit, den ich bearbeite, sha1 65dfb6a . Wenn ich das weiß, kann ich den Inhalt dieses Commits über mein Arbeitsverzeichnis mit dem Formular git checkout das sowohl einen Commit- als auch einen Dateispeicherort benötigt. Hier benutze ich . als Speicherort für die gesamte Arbeitskopie:

 git checkout 65dfb6a . 

Verpasse nicht den Punkt am Ende!

Dadurch werden die Dateien wie nach der von Ihnen bearbeiteten Festschreibung ausgecheckt und auf die Bühne gebracht, aber relativ zu der vorherigen Festschreibung, die Sie vorgenommen haben. Daher sind Änderungen, die Sie bereits vorgenommen haben, nicht Teil der Festschreibung.

Sie können entweder jetzt fortfahren und es sozusagen übernehmen, um die Aufteilung zu beenden, oder Sie können erneut umhergehen, indem Sie einige Teile des Commits löschen, bevor Sie einen weiteren vorläufigen Commit durchführen.

Wenn Sie die ursprüngliche Commit-Nachricht für ein oder mehrere Commits wiederverwenden möchten, können Sie sie direkt aus den Arbeitsdateien der Rebase verwenden:

 git commit --file .git/rebase-merge/message 

Sobald Sie alle Änderungen vorgenommen haben,

 git rebase --continue 

wird weitermachen und den Rebase-Vorgang abschließen.

git rebase --interactive kann verwendet werden, um einen Commit in kleinere Commits aufzuteilen. Die Git-Dokumentation zu Rebase hat eine kurze Anleitung zum process – Splitting Commits :

Im interaktiven Modus können Sie Commits mit der Aktion “Bearbeiten” markieren. Dies bedeutet jedoch nicht unbedingt, dass git rebase erwartet, dass das Ergebnis dieser Bearbeitung genau ein Commit ist. In der Tat können Sie das Commit rückgängig machen oder andere Commits hinzufügen. Dies kann verwendet werden, um ein Commit in zwei Teile aufzuteilen:

  • Starten Sie ein interaktives Rebase mit git rebase -i ^ , wobei das Commit ist, das Sie teilen möchten. In der Tat wird jeder Commit-Bereich ausreichen, solange er diesen Commit enthält.

  • Markieren Sie das Commit, das Sie teilen möchten, mit der Aktion “Bearbeiten”.

  • Wenn Sie diesen Commit bearbeiten, führen Sie git reset HEAD^ . Der Effekt ist, dass der HEAD um eins zurückgespult wird und der Index folgt. Der Arbeitsbaum bleibt jedoch gleich.

  • Fügen Sie nun die Änderungen dem Index hinzu, den Sie beim ersten Commit haben möchten. Sie können git add (möglicherweise interaktiv) oder git gui (oder beides) verwenden, um das zu tun.

  • Übergeben Sie den jetzt aktuellen Index mit der aktuellen Commit-Nachricht.

  • Wiederholen Sie die letzten beiden Schritte, bis Ihr Arbeitsbaum sauber ist.

  • Setzen Sie die Rebase mit git rebase --continue .

Wenn Sie nicht absolut sicher sind, dass die Zwischenrevisionen konsistent sind (sie kompilieren, die Testsuite übergeben usw.), sollten Sie git stash , um die noch nicht festgeschriebenen Änderungen nach jedem Festschreiben, Testen und Ändern der Festschreibung zu speichern sind notwendig.

Sie können interaktive Rebase git rebase -i tun. Die Manpage hat genau das, was du willst:

http://git-scm.com/docs/git-rebase#_splitting_commits

Bitte beachten Sie, dass es auch git reset --soft HEAD^ . Es ähnelt git reset (standardmäßig --mixed ), behält aber den --mixed . Wenn Sie also Dateien hinzugefügt / entfernt haben, haben Sie diese bereits im Index.

Es erweist sich als sehr nützlich im Falle von Giganten.

Jetzt in der neuesten TortoiseGit unter Windows können Sie es sehr einfach tun.

Öffnen Sie das Dialogfeld “Rebase”, konfigurieren Sie es und führen Sie die folgenden Schritte aus.

  • Klicken Sie mit der rechten Maustaste auf das Commit, das Sie teilen möchten, und wählen Sie ” Edit ” (unter “Auswählen”, “Squash”, “Löschen” …).
  • Klicken Sie auf ” Start “, um mit der Neuausrichtung zu beginnen.
  • Sobald es zu dem zu teilenden Commit kommt, klicken Sie auf die Schaltfläche ” Edit/Split ” und klicken Sie direkt auf ” Amend “. Der Bestätigungsdialog wird geöffnet.
    Bearbeite / Teilen
  • Heben Sie die Auswahl der Dateien auf, die Sie einem separaten Commit hinzufügen möchten.
  • Bearbeiten Sie die Commit-Nachricht und klicken Sie dann auf ” commit “.
  • Bis Dateien zu committen sind, wird der Commit-Dialog immer wieder geöffnet. Wenn keine Datei mehr zu committen ist, werden Sie immer noch gefragt, ob Sie eine weitere Commit hinzufügen möchten.

Sehr hilfreich, danke TortoiseGit!

Ich denke, dass der beste Weg, ich benutze git rebase -i . Ich habe ein Video erstellt, um die Schritte zum Teilen eines Commits zu zeigen: https://www.youtube.com/watch?v=3EzOz7e1ADI

Das einfachste Ding, ohne eine interaktive Rebase zu tun, ist (wahrscheinlich) einen neuen Zweig zu machen, der bei dem Commit vor demjenigen beginnt, den man teilen möchte, cherry-pick -n das Commit, Reset, Stash, Commit der Datei move, Reapply the stash und Übernehmen Sie die Änderungen und merge Sie dann entweder mit dem vorherigen Zweig oder wählen Sie die folgenden Befehle aus. (Wechseln Sie dann den Namen der ehemaligen Zweigstelle in den aktuellen Kopf.) (Es ist wahrscheinlich besser, den MBO-Ratschlägen zu folgen und eine interaktive Neufassung vorzunehmen.)

Wenn du das hast:

 A - B < - mybranch 

Wenn Sie in Commit B einen Inhalt festgelegt haben:

 /modules/a/file1 /modules/a/file2 /modules/b/file3 /modules/b/file4 

Aber Sie möchten B in C - D aufteilen und erhalten dieses Ergebnis:

 A - C - D < -mybranch 

Sie können den Inhalt beispielsweise teilen (Inhalt aus verschiedenen Verzeichnissen in verschiedenen Commits) ...

Setzen Sie den Zweig zurück auf das Commit vor dem zu teilenden Commit:

 git checkout mybranch git reset --hard A 

Erstes Commit erstellen (C):

 git checkout B /modules/a git add -u git commit -m "content of /modules/a" 

Zweites Commit erstellen (D):

 git checkout B /modules/b git add -u git commit -m "content of /modules/b"