Wie kann ich zwei Commits zu einem zusammenführen, wenn ich bereits mit dem Rebase begonnen habe?

1158

Ich versuche, 2 Commits zu 1 zusammenzuführen, also habe ich "Squashing Commits mit Rebase" von git ready verfolgt .

ich rannte

git rebase --interactive HEAD~2

Im resultierenden Editor wechsle ich pickzu squashund speichere dann, aber die Rebase schlägt mit dem Fehler fehl

Ohne vorheriges Festschreiben kann nicht "gequetscht" werden

Nachdem mein Arbeitsbaum diesen Zustand erreicht hat, kann ich mich nicht mehr erholen.

Der Befehl git rebase --interactive HEAD~2schlägt fehl mit:

Die interaktive Rebase wurde bereits gestartet

und git rebase --continuescheitert mit

Ohne vorheriges Festschreiben kann nicht "gequetscht" werden

Michael
quelle
22
Ich habe das auch getroffen. Mein Fehler wurde durch die Tatsache verursacht, dass git rebase -i die Commits in der entgegengesetzten Reihenfolge des Git-Protokolls auflistet. Das letzte Commit ist ganz unten!
lmsurprenant
1
Schauen
nha

Antworten:

1733

Zusammenfassung

Die Fehlermeldung

Ohne vorheriges Festschreiben kann nicht "gequetscht" werden

bedeutet, dass Sie wahrscheinlich versucht haben, "nach unten zu quetschen". Git zerquetscht ein neueres Commit immer in ein älteres Commit oder "aufwärts", wie in der interaktiven Rebase-Aufgabenliste angezeigt, dh in einem Commit in einer vorherigen Zeile. Wenn Sie den Befehl in der ersten Zeile Ihrer Aufgabenliste in ändern, squashwird immer dieser Fehler ausgegeben, da für das erste Commit nichts zum Squash vorhanden ist.

Die Reparatur

Kommen Sie zuerst dorthin zurück, wo Sie angefangen haben

$ git rebase --abort

Sagen Sie, Ihre Geschichte ist

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

Das heißt, a war das erste Commit, dann b und schließlich c. Nachdem wir c festgelegt haben, beschließen wir, b und c zusammen zu quetschen:

(Hinweis: Beim Ausführen von git logPipes wird die Ausgabe lessstandardmäßig auf den meisten Plattformen in einen Pager geleitet . Um den Pager zu beenden und zur Eingabeaufforderung zurückzukehren, drücken Sie die qTaste.)

Laufen git rebase --interactive HEAD~2gibt Ihnen einen Editor mit

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Beachten Sie, dass diese Aufgabenliste im Vergleich zur Ausgabe von in umgekehrter Reihenfolge ist git log.)

Ändern bs führt zum Fehler , den Sie gesehen haben, aber wenn Sie stattdessen c in b zerquetschen (neuere begeht in die älteren oder „Quetschen nach oben“) , indem Sie die To - do - Liste zu ändern , umpicksquash

pick   b76d157 b
squash a931ac7 c

Wenn Sie Ihren Editor speichern, erhalten Sie einen anderen Editor, dessen Inhalt ist

# This is a combination of 2 commits.
# The first commit's message is:

b

# This is the 2nd commit message:

c

Wenn Sie speichern und beenden, wird der Inhalt der bearbeiteten Datei zur Festschreibungsnachricht des neuen kombinierten Festschreibens:

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Hinweis zum Umschreiben des Verlaufs

Interaktive Rebase schreibt den Verlauf neu. Der Versuch, auf eine Fernbedienung zu pushen, die den alten Verlauf enthält, schlägt fehl, da es sich nicht um einen schnellen Vorlauf handelt.

Wenn der Zweig, den Sie neu gegründet haben, ein Thema oder ein Feature-Zweig ist, in dem Sie selbst arbeiten , ist das keine große Sache. Für das Verschieben in ein anderes Repository ist die --forceOption erforderlich. Abhängig von den Berechtigungen des Remote-Repositorys können Sie möglicherweise zuerst den alten Zweig löschen und dann die neu basierte Version übertragen. Beispiele für Befehle, die möglicherweise die Arbeit zerstören, sind nicht Gegenstand dieser Antwort.

Umschreiben von bereits veröffentlichten Geschichte auf einem Zweig , in dem Sie mit anderen Menschen arbeiten , ohne sehr guten Grund , wie ein Passwort oder andere sensible Details Kräfte arbeiten auf Ihre Mitarbeiter undicht und ist unsozial und andere Entwickler ärgern. Der Abschnitt „Wiederherstellen von einer Upstream-Rebase“ in der git rebaseDokumentation wird mit zusätzlichem Schwerpunkt erläutert.

Das Wiederherstellen (oder jede andere Form des Umschreibens) eines Zweigs, auf dem andere basieren, ist eine schlechte Idee: Jeder, der ihm nachgeschaltet ist, muss seinen Verlauf manuell korrigieren. In diesem Abschnitt wird erläutert, wie die Korrektur aus Sicht des Downstreams durchgeführt wird. Die eigentliche Lösung wäre jedoch, zu vermeiden, dass der Upstream überhaupt neu aufgebaut wird.

Greg Bacon
quelle
Wenn ich Rebase verwende, um ein Commit zu quetschen, wird ein neues "kombiniertes" Commit erstellt, das die beiden Änderungssätze enthält, aber der Hash ist unterschiedlich. Werden die ursprünglichen Commits auch von git beibehalten?
Fabsenet
@fabsenet Ja und nein. Die ursprünglichen Commits sind weiterhin verfügbar, aber wahrscheinlich nicht mehr von einem Ref aus erreichbar (abhängig von den Angaben in Ihrer Historie). Nicht referenzierte Commits werden schließlich durch den Garbage Collection-Prozess gelöscht.
Greg Bacon
Ich habe nur git log hashoftheoldcommitgit log --graph
rumgespielt
Dieser Squash ist ein gutes Werkzeug, um Commits vor dem Push zu organisieren, aber wenn ich einen der Commits pushe, kann ich ihn nicht quetschen? Git sagt: Erfolgreich neu basiert und losgelöst HEAD aktualisiert.
Sérgio
Ich bekomme den Editor nicht auf die zweite Instanz, die Git Bash, scheint in einem Prozess eingefroren zu sein. Was ist zu tun?
411

Wenn mehrere Commits vorhanden sind, können Sie git rebase -izwei Commits zu einem zusammenfassen.

Wenn Sie nur zwei Commits zusammenführen möchten und es sich um die "letzten zwei" handelt, können Sie die beiden Commits mit den folgenden Befehlen zu einem kombinieren:

git reset --soft "HEAD^"
git commit --amend
user3828059
quelle
6
Was ist der Nachteil im Vergleich zu Rebase? Ich finde eine viel einfacher zu bedienen.
Guillaume86
17
Sie können nicht in beliebiger Reihenfolge beitreten - nur die letzten beiden Commits .
dr0i
50
@ dr0i Sie können so viele Commits zusammenführen, wie Sie möchten, solange es sich um die letzten X-Commits handelt und nicht irgendwo in der Mitte. Führen Sie einfach aus git reset --soft HEAD~10, wobei 10 die Anzahl der Commits ist, die Sie zusammenführen möchten.
fregante
2
Dies kann verwendet werden, wenn Sie keinen Remote-Ursprung festgelegt haben und nur zwei Commits haben.
Atedja
8
Sie können auch verpflichten, eine bestimmte zurücksetzen , wenn Sie nicht zählen tun wollen , wie viele es von der sind HEADdurch die Verwendung , git reset --soft 47b5c5...wo 47b5c5...die SHA1 ist ID des begehen.
dguay
112

Rebase: Du wirst es nicht brauchen:

Ein einfacherer Weg für das häufigste Szenario.

In den meisten Fällen:

Eigentlich, wenn alles, was Sie wollen, einfach mehrere aktuelle Commits zu einem kombinieren, aber nicht benötigen drop, rewordund andere Rebase-Arbeiten.

Sie können einfach tun:

git reset --soft "HEAD~n"
  • Unter der Annahme , ~nist die Anzahl der Commits leise un-commit (dh ~1, ~2...)

Verwenden Sie dann den folgenden Befehl, um die Festschreibungsnachricht zu ändern.

git commit --amend

Das ist so ziemlich das Gleiche wie eine große Reichweite von squashund eins pick.

Und es funktioniert für n Commits, aber nicht nur für zwei Commits, wie oben angegeben.

pambda
quelle
3
Dies ist hilfreich, wenn Sie neben dem Quetschen der Commits auch eine zusätzliche Bereinigung durchführen möchten, z. B. das Entfernen von 1 Commit in der Mitte oder das Ändern einer Codezeile.
Styfle
1
Unter der Annahme , ~nist die Anzahl der Commits leise un-commit (dh ~1, ~2...)
Ray
1
Was ist, wenn ich nicht die nletzten Commits zusammenführen möchte , sondern die nCommits in der Mitte? Kann ich das so einfach machen?
Chumakoff
1
Dann git rebase -imüssen Sie squasharbeiten. @ Chumakoff
Pambda
3
So beitreten nletzten Commits in einem, dem ersten Gebrauch git reset --soft @~m, wom = n - 1
Łukasz Rajchel
55

Zuerst sollten Sie überprüfen, wie viele Commits Sie haben:

git log

Es gibt zwei Status:

Zum einen gibt es nur zwei Commits:

Zum Beispiel:

commit A
commit B

(In diesem Fall können Sie git rebase nicht verwenden.) Sie müssen Folgendes tun.

$ git reset --soft HEAD^1

$ git commit --amend

Ein weiterer Grund ist, dass es mehr als zwei Commits gibt. Sie möchten Commit C und D zusammenführen.

Zum Beispiel:

commit A
commit B
commit C
commit D

(Unter dieser Bedingung können Sie Git Rebase verwenden.)

git rebase -i B

Und dann "Squash" verwenden. Der Rest ist sehr einfach. Wenn Sie es immer noch nicht wissen, lesen Sie bitte http://zerodie.github.io/blog/2012/01/19/git-rebase-i/

Haimei
quelle
Das Zurücksetzen --soft und commit --amend ist die einzige Möglichkeit, die funktioniert, wenn bereits eine Rebase ausgeführt wird (und für dieses Commit 'edit' anstelle von 'squash' gewählt wurde). +1
Jacek Lach
1
Das Zusammenführen der ersten und einzigen zwei Commits in einem Repository, genau mein Randfall :-)
Chris Huang-Leaver
1
Bitte fügen Sie hinzu, dass git push -f origin masterdies erforderlich sein könnte.
Rishabh Agrahari
33

Angenommen, Sie waren in Ihrem eigenen Themenbereich. Wenn Sie die letzten 2 Commits zu einem zusammenführen und wie ein Held aussehen möchten, verzweigen Sie das Commit kurz bevor Sie die letzten beiden Commits durchgeführt haben.

git checkout -b temp_branch HEAD^2

Dann schreibt Squash den anderen Zweig in diesem neuen Zweig fest:

git merge branch_with_two_commits --squash

Das wird die Änderungen einbringen, aber nicht festschreiben. Also legen Sie sie einfach fest und Sie sind fertig.

git commit -m "my message"

Jetzt können Sie diesen neuen Themenzweig wieder in Ihrem Hauptzweig zusammenführen.

Homan
quelle
5
Dies war eigentlich die hilfreichste Antwort für mich, da keine manuelle Neubasierung erforderlich war, sondern nur alle Commits eines ganzen Zweigs zu einem Commit zusammengefasst wurden. Sehr schön.
Robert
Danke dafür! So kann ich Git dazu bringen, wie ich mir Squashing Commits in meinem Kopf vorstelle!
Marjan Venema
erstaunliche Antwort, so viel einfacher als die Alternativen
Offenbar ist diese Antwort für den Fall nicht geeignet , wenn aund cerfordert zusammen werden verschmolzen und zu halten , bwie es ist.
Talha Ashraf
2
Hat sich in den letzten Versionen von git etwas geändert? Wenn ich den ersten Befehl ( git checkout -b combine-last-two-commits "HEAD^2") in Git Version 2.17 versuche , erhalte ich eine Fehlermeldung:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
Mhucka
23

Sie können die Rebase mit abbrechen

git rebase --abort

und wenn Sie den interaktiven Rebase-Befehl erneut ausführen, wird der 'Squash; Commit muss unter dem Pick Commit in der Liste liegen

Leom Burke
quelle
16

Ich verwende häufig git reset --mixed , um eine Basisversion vor mehreren Commits, die Sie zusammenführen möchten, zurückzusetzen. Dann mache ich ein neues Commit. Auf diese Weise kann Ihr Commit das neueste sein und sicherstellen, dass Ihre Version HEAD ist, nachdem Sie auf den Server übertragen haben.

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Wenn ich zwei Commits zu einem zusammenführen möchte, verwende ich zuerst:

git reset --mixed 249cf9392da197573a17c8426c282

"249cf9392da197573a17c8426c282" war die dritte Version, ist auch Ihre Basisversion, bevor Sie zusammenführen. Danach mache ich ein neues Commit:

git add .
git commit -m 'some commit message'

Es ist alles, Hoffnung ist ein anderer Weg für alle.

Zu Ihrer Information, von git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.
VinceStyling
quelle
Ich habe die Dokumente für '--mixed' nicht gelesen, aber ich bin sicher, dass andere Leute den Beitrag gelesen haben und sich das Gleiche gefragt haben: Was ist der Vorteil der Verwendung von --mixed? Könnte Ihren Beitrag verbessern, um einen Ausschnitt aus der Manpage aufzunehmen.
Funroll
@funroll Ich wusste es nicht - sehr viel gemischt, bevor ich diese Antwort schreibe. Nach meiner eigenen Erfahrung wird die gemischte Operation eine bestimmte Version angeben, die ich als Argument als Repository-HEAD-Version übergebe, und nach dieser Version kann nichts verloren gehen. Wir können also immer noch mit diesen Änderungen umgehen.
VinceStyling
14

$ git rebase --abort

Führen Sie diesen Code jederzeit aus, wenn Sie die Git-Rebase rückgängig machen möchten

$ git rebase -i HEAD~2

Die letzten beiden Commits erneut anwenden. Der obige Befehl öffnet einen Code-Editor

  • [ Das letzte Commit befindet sich unten ]. Ändern Sie das letzte Commit in Squash (s). Da wird Squash mit vorherigem Commit verschmelzen.
  • Drücken Sie dann die Esc-Taste und geben Sie Folgendes ein: wq zum Speichern und Schließen

Nach: wq befinden Sie sich im aktiven Rebase-Modus

Hinweis : Sie erhalten einen anderen Editor, wenn keine Warn- / Fehlermeldungen angezeigt werden. Wenn ein Fehler oder eine Warnung eines anderen Editors nicht angezeigt wird, können Sie den Vorgang durch Ausführen abbrechen,$ git rebase --abortwenn Sie einen Fehler oder eine Warnung sehen.Andernfalls fahren Sie einfach mit der Ausführung fort$ git rebase --continue

Sie sehen Ihre 2 Commit-Nachricht. Wählen Sie eine aus oder schreiben Sie Ihre eigene Commit-Nachricht, speichern Sie sie und beenden Sie [: wq]

Hinweis 2: Möglicherweise müssen Sie Ihre Änderungen zwangsweise auf das Remote-Repo übertragen, wenn Sie den Befehl rebase ausführen

$ git push -f

$ git push -f origin master

Gnanasekar S.
quelle
1
Anmerkung 2: git push -f origin/masterAndere Antworten fehlen. +1
Rishabh Agrahari
2

Da ich git cherry-pickfür fast alles benutze , ist es für mich selbstverständlich, dies auch hier zu tun.

Da ich branchXausgecheckt habe und zwei Commits an der Spitze stehen, von denen ich ein Commit erstellen möchte, das ihren Inhalt kombiniert, mache ich Folgendes:

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Wenn ich auch aktualisieren möchte branchX(und ich nehme an, dass dies der Nachteil dieser Methode ist), muss ich auch:

git checkout branchX
git reset --hard <the_new_commit>
Martin G.
quelle
1

Wenn Ihre Hauptniederlassung git logungefähr so ​​aussieht:

commit ac72a4308ba70cc42aace47509a5e
Author: <[email protected]>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <[email protected]>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <[email protected]>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

und Sie möchten die beiden obersten Commits zusammenführen, indem Sie einfach die folgenden einfachen Schritte ausführen:

  1. Um zuerst auf der sicheren Seite zu sein, checken Sie das vorletzte Commit in einem separaten Zweig aus. Sie können den Zweig beliebig benennen.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Wählen Sie jetzt einfach Ihre Änderungen aus dem letzten Commit in diesem neuen Zweig aus als : git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e. (Konflikte lösen, falls vorhanden)
  3. Jetzt sind Ihre Änderungen im letzten Commit in Ihrem vorletzten Commit enthalten. Sie müssen sich jedoch noch festlegen. Fügen Sie also zuerst die Änderungen hinzu, die Sie gerade ausgewählt haben, und führen Sie sie dann aus git commit --amend.

Das ist es. Sie können diese zusammengeführte Version in den Zweig "Merged-Commits" verschieben, wenn Sie möchten.

Außerdem können Sie jetzt zwei aufeinanderfolgende Commits in Ihrem Hauptzweig verwerfen. Aktualisieren Sie einfach Ihren Hauptzweig wie folgt:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull
Usman
quelle
0

Wenn Sie die beiden letzten Commits kombinieren und nur die Nachricht des älteren Commits verwenden möchten, können Sie den Prozess mithilfe automatisieren expect.

Ich nehme an:

  • Sie verwenden vi als Editor
  • Ihre Commits sind jeweils einzeilig

Ich habe mit getestet git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact
erwaman
quelle
Es ist unklar, was Ihr Skript tut.
27.
@buhtz Ich habe noch einige Kommentare hinzugefügt. Lassen Sie mich wissen, wenn Sie es immer noch verwirrend finden, und wenn ja, welchen Teil.
Erwaman
Es ist immer noch unklar, was Ihr Scrpit tut. Auch expectist unbeschrieben.
28.
@buhtz welcher Teil ist unklar? Ich habe einen Link zu einer Seite mit mehr Dokumentation für bereitgestellt expect.
Erwaman
Eine allgemeine Beschreibung des Skripts fehlt. Es ist unklar, was es tut. Kein Teil ist unklar. Die Absicht des Skripts selbst ist unklar.
Buhz