So teilen Sie das letzte Commit in Git in zwei Teile

277

Ich habe zwei funktionierende Zweige, Master und Forum, und ich habe gerade einige Änderungen im Forum- Zweig vorgenommen, die ich gerne in Master auswählen würde . Leider enthält das Commit, das ich auswählen möchte, auch einige Änderungen, die ich nicht möchte.

Die Lösung wäre wahrscheinlich, das falsche Commit irgendwie zu löschen und durch zwei separate Commits zu ersetzen, eines mit Änderungen, die ich im Master auswählen möchte, und andere, die nicht dorthin gehören.

Ich habe es versucht

git reset --hard HEAD^

das löschte alle Änderungen, also musste ich mit zurückgehen

git reset ORIG_HEAD

Also meine Frage ist, was ist die beste Art und Weise zu spalten letzten Commit in zwei separate Commits?

Jakub Arnold
quelle

Antworten:

332

Sie sollten den Index verwenden. Fügen Sie nach einem gemischten Reset (" git reset HEAD ^") die ersten Änderungen in den Index ein und schreiben Sie sie fest. Dann legen Sie den Rest fest.

Mit " git add " können Sie alle in einer Datei vorgenommenen Änderungen in den Index einfügen. Wenn Sie nicht jede in einer Datei vorgenommene Änderung vornehmen möchten, sondern nur einige, können Sie "git add -p" verwenden.

Sehen wir uns ein Beispiel an. Nehmen wir an, ich hätte eine Datei namens myfile, die den folgenden Text enthält:

something
something else
something again

Ich habe es in meinem letzten Commit so geändert, dass es jetzt so aussieht:

1
something
something else
something again
2

Jetzt entscheide ich, dass ich es in zwei Teile teilen möchte, und ich möchte, dass die Einfügung der ersten Zeile im ersten Commit und das Einfügen der letzten Zeile im zweiten Commit erfolgt.

Zuerst gehe ich zurück zu HEADs Eltern, aber ich möchte die Änderungen im Dateisystem beibehalten, also verwende ich "git reset" ohne Argument (was einen sogenannten "gemischten" Reset bewirkt):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

Jetzt benutze ich "git add -p", um die Änderungen, die ich in den Index übernehmen möchte, hinzuzufügen (= ich stelle sie bereit). "git add -p" ist ein interaktives Tool, das Sie fragt, welche Änderungen an der Datei vorgenommen werden sollen, wenn sie dem Index hinzugefügt werden.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

Dann begebe ich diese erste Änderung:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

Jetzt kann ich alle anderen Änderungen übernehmen (nämlich die in die letzte Zeile eingegebene Ziffer "2"):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

Lassen Sie uns das Protokoll überprüfen, um festzustellen, welche Commits wir haben:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
hcs42
quelle
1
Ich habe mich in den letzten anderthalb Wochen langsam daran gewöhnt, von Mercurial zu spielen, und es gibt einen praktischen Verknüpfungsbefehl git reset [--patch|-p] <commit>, mit dem Sie sich die Mühe ersparen können, die Sie git add -pnach dem Zurücksetzen benötigen. Habe ich recht? Verwenden von git 1.7.9.5.
Trojjer
2
Im Folgenden finden Sie weitere Informationen zu dieser Technik, einschließlich der Neubasierung, wenn es sich um ein älteres Commit handelt, oder wenn Sie N Commits in M ​​Commits ändern müssen : emmanuelbernard.com/blog/2014/04/14/… .
Chris Westin
84

Tore:

  • Ich möchte ein vergangenes Commit ( splitme) in zwei Teile teilen .
  • Ich möchte die Festschreibungsnachricht beibehalten .

Planen:

  1. Rebase interaktiv von einem vor splitme.
  2. bearbeiten splitme.
  3. Setzen Sie die Dateien zurück, um sie in ein zweites Commit aufzuteilen.
  4. Commit ändern, Nachricht pflegen, nach Bedarf ändern.
  5. Fügen Sie die vom ersten Commit aufgeteilten Dateien wieder hinzu.
  6. Mit einer neuen Nachricht festschreiben.
  7. Rebase fortsetzen.

Die Rebase-Schritte (1 & 7) können übersprungen werden, wenn dies das splitmeletzte Commit ist.

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

Wenn ich wollte, dass die geteilten Dateien zuerst festgeschrieben werden, würde ich dann -i erneut neu starten und die Reihenfolge ändern

git rebase -i splitme^
# swap order of splitme and 'just some files'
spazm
quelle
1
git reset HEAD^war das fehlende Puzzleteil. Funktioniert auch gut mit -p. Vielen Dank!
Marius Gedminas
10
Es ist wichtig, das -- $filesArgument zu beachten git reset. git resetStellt diese Dateien bei übergebenen Pfaden den Status des referenzierten Commits wieder her, ändert jedoch keine Commits. Wenn Sie die Pfade weglassen, "verlieren" Sie das Commit, das Sie im nächsten Schritt ändern möchten.
Duelin Marker
2
Diese Methode verhindert, dass Sie Ihre erste Festschreibungsnachricht im Vergleich zur akzeptierten Antwort erneut kopieren und einfügen müssen.
Calvin
Außerdem: Wenn Sie alle Dateien zurücksetzen möchten, verwenden Sie einfach git reset HEAD^ -- .. Sehr überraschend ist dies nicht genau das Verhalten von git reset HEAD^.
Allidoiswin
52

Um das aktuelle Commit in zwei Commits zu ändern, können Sie Folgendes tun.

Entweder:

git reset --soft HEAD^

Dies macht das letzte Commit rückgängig, lässt aber alles inszeniert. Sie können dann bestimmte Dateien entfernen:

git reset -- file.file

Optional können Teile dieser Dateien wiederhergestellt werden:

git add -p file.file

Machen Sie ein neues erstes Commit:

git commit

Die Phase und das Festschreiben der restlichen Änderungen in einem zweiten Festschreiben:

git commit -a

Oder:

Machen Sie alle Änderungen gegenüber dem letzten Commit rückgängig und entfernen Sie sie:

git reset HEAD^

Führen Sie die erste Änderungsrunde selektiv durch:

git add -p

Verpflichten:

git commit

Übernehmen Sie den Rest der Änderungen:

git commit -a

(Wenn Sie in einem der beiden Schritte ein Commit rückgängig machen, das eine brandneue Datei hinzugefügt hat, und dieses zum zweiten Commit hinzufügen möchten, müssen Sie es manuell hinzufügen, da commit -anur Änderungen an bereits verfolgten Dateien schrittweise vorgenommen werden.)

CB Bailey
quelle
22

Führen git guiSie " Ausführen " aus, aktivieren Sie das Optionsfeld "Letztes Commit ändern" und deaktivieren Sie (Commit> Unstage From Commit oder Ctrl- U) Änderungen, die Sie nicht in das erste Commit übernehmen möchten. Ich denke, das ist der einfachste Weg, dies zu tun.

Sie können die Änderung auch ohne Festschreiben ( git cherry-pick -n) git guiauswählen und dann entweder manuell oder mit den gewünschten Änderungen auswählen, bevor Sie sie festschreiben.

Michael Krelin - Hacker
quelle
15
git reset HEAD^

Das --hard ist das, was deine Veränderungen tötet.

Semanticart
quelle
13

Ich bin überrascht, dass niemand vorgeschlagen hat git cherry-pick -n forum. Dadurch werden die Änderungen aus dem letzten forumCommit bereitgestellt , aber nicht festgeschrieben. Sie können dann resetdie nicht benötigten Änderungen entfernen und festschreiben, was Sie behalten möchten.

Dahlbyk
quelle
3

Die Double-Revert-Squash-Methode

  1. Machen Sie ein weiteres Commit, das die unerwünschten Änderungen entfernt. (Wenn es sich um eine Datei handelt, ist dies sehr einfach: git checkout HEAD~1 -- files with unwanted changesund git commit. Wenn nicht, können Dateien mit gemischten Änderungen teilweise git reset fileund git add -p fileals Zwischenschritt bereitgestellt werden.) Nennen Sie dies das Zurücksetzen .
  2. git revert HEAD- Machen Sie noch ein Commit, das die unerwünschten Änderungen wieder hinzufügt. Dies ist die doppelte Rückkehr
  3. Von den 2 Commits, die Sie jetzt gemacht haben, quetschen Sie den ersten auf den Commit to split ( git rebase -i HEAD~3). Dieses Commit wird jetzt frei von unerwünschten Änderungen, da diese im zweiten Commit enthalten sind.

Leistungen

  • Erhält die Festschreibungsnachricht
  • Funktioniert auch dann, wenn das Commit zum Teilen nicht das letzte ist. Es ist nur erforderlich, dass die unerwünschten Änderungen nicht mit späteren Festschreibungen in Konflikt stehen
user2394284
quelle
1

Da Sie Kirschen pflücken, können Sie:

  1. cherry-pick es mit --no-commit Option hinzugefügt.
  2. resetund verwenden add --patch, add --editoder nuradd zu Stufe , was Sie wollen halten.
  3. commit die inszenierten Änderungen.
    • Um die ursprüngliche Festschreibungsnachricht wiederzuverwenden, können Sie dem Befehl --reuse-message=<old-commit-ref>oder --reedit-message=<old-commit-ref>Optionen hinzufügen commit.
  4. Blasen Sie nicht inszenierte Änderungen mit weg reset --hard.

Eine andere Möglichkeit, die ursprüngliche Festschreibungsnachricht beizubehalten oder zu bearbeiten:

  1. cherry-pick das ursprüngliche Commit wie gewohnt.
  2. Kehren Sie die Änderungen um, die Sie nicht möchten, und verwenden Sie sie add, um die Umkehrung durchzuführen.
    • Dieser Schritt wäre einfach, wenn Sie das entfernen, was Sie hinzugefügt haben, aber etwas schwierig, wenn Sie das hinzufügen, was Sie entfernt haben, oder eine Änderung rückgängig machen.
  3. commit --amend um die Umkehrung des von Kirschen gepflückten Commits zu bewirken.
    • Sie erhalten erneut dieselbe Festschreibungsnachricht, die Sie bei Bedarf behalten oder überarbeiten können.
ADTC
quelle
0

Dies könnte eine weitere Lösung sein, die für Fälle vorgesehen ist, in denen ein großer Commit vorliegt und eine kleine Menge von Dateien in einen neuen Commit verschoben werden muss. Dies funktioniert, wenn eine Reihe von<path> Dateien aus dem letzten Commit bei HEAD extrahiert und alle in ein neues Commit verschoben werden sollen. Wenn mehrere Commits erforderlich sind, können die anderen Lösungen verwendet werden.

Erstellen Sie zunächst Patches in den bereitgestellten und nicht bereitgestellten Bereichen, die die Änderungen enthalten, auf die der Code vor und nach der Änderung zurückgesetzt werden soll:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

Um zu verstehen, was passieren wird (Pfeil und Kommentare sind nicht Teil des Befehls):

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

<path>Änderungen im letzten Commit rückgängig machen :

git commit --amend  -> reverts changes on HEAD by amending with staged changes

Erstellen Sie ein neues Commit mit <path>Änderungen:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

Dies hat zur Folge, dass ein neues Commit erstellt wird, das die Änderungen enthält, die aus dem letzten Commit extrahiert wurden.

Jose Cifuentes
quelle