Squash meine letzten X Commits zusammen mit Git

3588

Wie kann ich meine letzten X-Commits mit Git zu einem Commit zusammenfassen?

Markdorison
quelle
12
Ähnliche Frage: stackoverflow.com/questions/7275508/…
koppor
2
@matt TortoiseGit ist dein Werkzeug. Es bietet eine einzige Funktion "Zu einem Commit kombinieren", die alle Schritte automatisch im Hintergrund aufruft. Leider nur für Windows verfügbar. Siehe meine Antwort unten.
Matthias M
1
Für Squashing bis zum ersten Commit siehe dies - stackoverflow.com/questions/1657017/…
goelakash
1
Nach dem Squash muss man Push Stackoverflow.com/questions/10298291/… erzwingen
Vikramvi

Antworten:

2091

Verwenden git rebase -i <after-this-commit>und ersetzen Sie "pick" beim zweiten und nachfolgenden Commit durch "squash" oder "fixup", wie im Handbuch beschrieben .

In diesem Beispiel <after-this-commit>ist dies entweder der SHA1-Hash oder die relative Position vom HEAD des aktuellen Zweigs, von dem aus Commits für den Rebase-Befehl analysiert werden. Wenn der Benutzer beispielsweise 5 Commits vom aktuellen HEAD in der Vergangenheit anzeigen möchte, lautet der Befehl git rebase -i HEAD~5.

Anomie
quelle
260
Dies, denke ich, beantwortet diese Frage ein bisschen besser stackoverflow.com/a/5201642/295797
Roy Truelove
92
Was ist damit gemeint <after-this-commit>?
2540625
43
<after-this-commit>ist Commit X + 1, dh übergeordnetes Element des ältesten Commits, das Sie quetschen möchten.
Jozek
339
Ich fand diese Antwort zu knapp, um sie eindeutig zu verstehen. Ein Beispiel hätte geholfen.
Ian Ollmann
54
Der Unterschied zwischen diesem rebase -iAnsatz und reset --softbesteht darin, rebase -idass ich den Commit-Autor behalten reset --softund erneut festlegen kann. Manchmal muss ich Commits von Pull-Anfragen quetschen und dabei die Autoreninformationen beibehalten. Manchmal muss ich Soft auf meine eigenen Commits zurücksetzen. Upvotes zu beiden tollen Antworten sowieso.
Zionyx
3829

Sie können dies ziemlich einfach ohne git rebaseoder tun git merge --squash. In diesem Beispiel werden die letzten 3 Commits gequetscht.

Wenn Sie die neue Festschreibungsnachricht von Grund auf neu schreiben möchten, reicht dies aus:

git reset --soft HEAD~3 &&
git commit

Wenn Sie mit dem Bearbeiten der neuen Festschreibungsnachricht mit einer Verkettung der vorhandenen Festschreibungsnachrichten beginnen möchten (dh ähnlich wie mit einer Befehlsliste pick / squash / squash /… / squash git rebase -i), müssen Sie diese Nachrichten extrahieren und übergeben sie zu git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Beide Methoden zerlegen die letzten drei Commits auf dieselbe Weise zu einem einzigen neuen Commit. Der Soft-Reset zeigt HEAD nur auf das letzte Commit, das Sie nicht quetschen möchten. Weder der Index noch der Arbeitsbaum werden vom Soft-Reset berührt, sodass der Index für Ihr neues Commit im gewünschten Zustand bleibt (dh er enthält bereits alle Änderungen gegenüber den Commits, die Sie "wegwerfen" möchten).

Chris Johnsen
quelle
163
Ha! Ich mag diese Methode. Es ist derjenige, der dem Geist des Problems nahe kommt. Schade, dass es so viel Voodoo erfordert. So etwas sollte einem der Grundbefehle hinzugefügt werden. Möglicherweise git rebase --squash-recentoder sogar git commit --amend-many.
Adrian Ratnapala
13
@ABB: Wenn für Ihren Zweig ein "Upstream" -Satz festgelegt ist, können Sie diesen möglicherweise verwenden branch@{upstream}(oder nur @{upstream}für den aktuellen Zweig; in beiden Fällen kann der letzte Teil mit abgekürzt werden @{u}; siehe Gitrevisions ). Dies kann von Ihrem „letzten Push-Commit“ abweichen (z. B. wenn jemand anderes etwas gepusht hat, das auf Ihrem letzten Push aufgebaut wurde, und Sie das dann abgerufen haben), aber es scheint, als ob es nahe an dem liegt, was Sie wollen.
Chris Johnsen
104
Das musste ich irgendwie, push -faber sonst war es schön, danke.
2rs2ts
39
@ 2rs2ts git push -f klingt gefährlich. Achten Sie darauf, nur lokale Commits zu quetschen. Berühren Sie niemals Push-Commits!
Matthias M
20
Ich muss auch git push --forcedanach verwenden, damit es das Commit dauert
Zach Saucier
740

Sie können dafür verwenden git merge --squash, was etwas eleganter ist als git rebase -i. Angenommen, Sie sind auf dem Master und möchten die letzten 12 Commits zu einem zusammenfassen.

WARNUNG: Stellen Sie zunächst sicher, dass Sie Ihre Arbeit festschreiben - überprüfen Sie, ob diese git statussauber ist (da git reset --hardinszenierte und nicht inszenierte Änderungen weggeworfen werden).

Dann:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

In der Dokumentation zugit merge wird die --squashOption ausführlicher beschrieben.


Update: Der einzige wirkliche Vorteil dieser Methode gegenüber dem git reset --soft HEAD~12 && git commitvon Chris Johnsen in seiner Antwort vorgeschlagenen einfacheren ist, dass Sie die Festschreibungsnachricht mit jeder Festschreibungsnachricht, die Sie quetschen, vorab ausgefüllt erhalten.

Mark Longair
quelle
16
Sie sagen, dies sei "eleganter" als git rebase -i, aber Sie geben keinen Grund dafür an. Dies vorläufig, weil es mir so scheint, als ob tatsächlich das Gegenteil der Fall ist und dies ein Hack ist; Führen Sie nicht mehr Befehle als nötig aus, nur um git mergeeines der Dinge zu erzwingen , für die git rebasees speziell entwickelt wurde?
Mark Amery
77
@ Mark Amery: Es gibt verschiedene Gründe, warum ich sagte, dass dies eleganter ist. Zum Beispiel müssen Sie nicht unnötig einen Editor erzeugen und dann in der "To-Do" -Datei nach einer Zeichenfolge suchen und diese ersetzen. Die Verwendung git merge --squashist auch in einem Skript einfacher zu verwenden. Im Wesentlichen war die Begründung, dass Sie dafür überhaupt nicht die "Interaktivität" benötigen git rebase -i.
Mark Longair
12
Ein weiterer Vorteil besteht darin, dass git merge --squashes angesichts von Verschiebungen / Löschungen / Umbenennungen weniger wahrscheinlich zu Zusammenführungskonflikten kommt als bei einer erneuten Basierung, insbesondere wenn Sie aus einem lokalen Zweig zusammenführen. (Haftungsausschluss: basierend auf nur einer Erfahrung, korrigieren Sie mich, wenn dies im allgemeinen Fall nicht wahr ist!)
Cheezmeister
2
Ich bin immer sehr zurückhaltend, wenn es um Hard-Resets geht - ich würde ein temporäres Tag verwenden, anstatt HEAD@{1}nur auf der sicheren Seite zu sein, z. B. wenn Ihr Workflow für eine Stunde durch einen Stromausfall usw. unterbrochen wird.
Tobias Kienzler
8
@ BT: Hast du dein Commit zerstört? :( Ich bin mir nicht sicher, was du damit meinst. Alles, was du begangen hast, kannst du leicht von Git's Reflog zurückbekommen. Wenn du nicht festgeschriebene Arbeit hattest, aber die Dateien inszeniert wurden, solltest du immer noch in der Lage sein, sie zu bekommen Ihre Inhalte sind zurück, obwohl das mehr Arbeit sein wird . Wenn Ihre Arbeit jedoch nicht einmal inszeniert wurde, kann leider wenig getan werden. Deshalb lautet die Antwort im Vorfeld: "Überprüfen Sie zuerst, ob der Git-Status sauber ist (seit git reset --hard wird inszenierte und nicht inszenierte Änderungen wegwerfen) ".
Mark Longair
218

Ich empfehle, git resetwenn möglich zu vermeiden - besonders für Git-Neulinge. Wenn Sie einen Prozess, der auf einer Reihe von Commits basiert, nicht wirklich automatisieren müssen , gibt es einen weniger exotischen Weg ...

  1. Legen Sie die zu quetschenden Commits auf einen funktionierenden Zweig (falls noch nicht geschehen) - verwenden Sie dazu gitk
  2. Überprüfen Sie den Zielzweig (z. B. 'Master').
  3. git merge --squash (working branch name)
  4. git commit

Die Festschreibungsnachricht wird basierend auf dem Squash vorab ausgefüllt.

kein Balken
quelle
4
Dies ist die sicherste Methode: kein Zurücksetzen weich / hart (!!) oder Reflog verwendet!
TeChn4K
14
Es wäre großartig, wenn Sie (1) erweitern würden.
Adam
2
@Adam: Grundsätzlich bedeutet dies, dass Sie die GUI-Oberfläche von verwenden gitk, um die Codezeile zu kennzeichnen, die Sie quetschen, und auch die Basis, auf der gequetscht werden soll. Im Normalfall sind diese beiden Beschriftungen bereits vorhanden, sodass Schritt (1) übersprungen werden kann.
Nobar
3
Beachten Sie, dass diese Methode den Arbeitszweig nicht als vollständig zusammengeführt markiert. Wenn Sie ihn entfernen, müssen Sie ihn löschen. :(
Kyrstellaine
2
Für (1) habe ich git branch your-feature && git reset --hard HEAD~Nden bequemsten Weg gefunden. Es wird jedoch erneut ein Git-Reset durchgeführt, was durch diese Antwort vermieden werden sollte.
EIS
132

Basierend auf der Antwort von Chris Johnsen ,

Fügen Sie einen globalen "Squash" -Alias ​​von bash hinzu: (oder Git Bash unter Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... oder über die Windows-Eingabeaufforderung:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Sie ~/.gitconfigsollten jetzt diesen Alias ​​enthalten:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Verwendungszweck:

git squash N

... die automatisch die letzten NCommits zusammenquetscht, einschließlich.

Hinweis: Die resultierende Festschreibungsnachricht ist eine Kombination aller gequetschten Festschreibungen in der angegebenen Reihenfolge. Wenn Sie damit nicht zufrieden sind, können Sie es jederzeit git commit --amendmanuell ändern. (Oder bearbeiten Sie den Alias ​​nach Ihrem Geschmack.)

EthanB
quelle
6
Interessant, aber ich würde die gequetschte Commit-Nachricht lieber selbst als beschreibende Zusammenfassung meiner mehreren Commits eingeben, als sie automatisch für mich eingeben zu lassen. Daher möchte ich lieber die Anzahl der nicht gepushten Commits angeben git squash -m "New summary."und Nautomatisch bestimmen.
Acumenus
1
@ABB, Das klingt nach einer separaten Frage. (Ich glaube nicht, dass es genau das ist, was das OP gefragt hat; ich habe in meinem Git-Squash-Workflow nie ein Bedürfnis danach verspürt.)
EthanB
3
Das ist ziemlich süß. Persönlich hätte ich gerne eine Version, die die Commit-Nachricht aus dem ersten der zusammengequetschten Commits verwendet. Wäre gut für Dinge wie Whitespace-Tweaks.
Funroll
@funroll Einverstanden. Nur die letzte Commit-Nachricht fallen zu lassen, ist für mich ein sehr häufiges Bedürfnis. Wir sollten in der Lage sein, das zu entwickeln ...
Steve Clay
2
Mit @ABB können Sie git commit --amenddie Nachricht weiter ändern. Mit diesem Alias ​​können Sie jedoch gut beginnen, was in der Festschreibungsnachricht enthalten sein soll.
Bindestrich
131

Dank dieses praktischen Blogposts habe ich festgestellt, dass Sie diesen Befehl verwenden können, um die letzten 3 Commits zu quetschen:

git rebase -i HEAD~3

Dies ist praktisch, da es auch dann funktioniert, wenn Sie sich in einer lokalen Filiale ohne Tracking-Informationen / Remote-Repo befinden.

Der Befehl öffnet den interaktiven Rebase-Editor, mit dem Sie wie gewohnt neu ordnen, quetschen, umformulieren usw. können.


Verwenden des interaktiven Rebase-Editors:

Der interaktive Rebase-Editor zeigt die letzten drei Commits an. Diese Einschränkung wurde durch HEAD~3Ausführen des Befehls bestimmt git rebase -i HEAD~3.

Das letzte Commit HEADwird zuerst in Zeile 1 angezeigt. Die mit a beginnenden Zeilen #sind Kommentare / Dokumentationen.

Die angezeigte Dokumentation ist ziemlich klar. In jeder Zeile können Sie den Befehl von pickin einen Befehl Ihrer Wahl ändern .

Ich bevorzuge die Verwendung des Befehls, fixupda dies die Änderungen des Commits in das Commit in der obigen Zeile "quetscht" und die Nachricht des Commits verwirft.

Da das Commit in Zeile 1 lautet HEAD, würden Sie dies in den meisten Fällen als belassen pick. Sie können nicht verwenden squashoder fixupda es kein anderes Commit gibt, in das Sie das Commit drücken können.

Sie können auch die Reihenfolge der Commits ändern. Auf diese Weise können Sie Commits quetschen oder korrigieren, die chronologisch nicht benachbart sind.

interaktiver Rebase-Editor


Ein praktisches Alltagsbeispiel

Ich habe kürzlich eine neue Funktion festgelegt. Seitdem habe ich zwei Bugfixes begangen. Aber jetzt habe ich einen Fehler (oder vielleicht nur einen Rechtschreibfehler) in der neuen Funktion entdeckt, die ich begangen habe. Wie nervig! Ich möchte kein neues Commit, das meine Commit-Historie verschmutzt!

Als erstes behebe ich den Fehler und mache ein neues Commit mit dem Kommentar squash this into my new feature!.

Ich starte dann git logoder gitkund erhalte die Commit-SHA der neuen Funktion (in diesem Fall 1ff9460).

Als nächstes rufe ich den interaktiven Rebase-Editor mit auf git rebase -i 1ff9460~. Die ~SHA nach dem Festschreiben weist den Editor an, dieses Festschreiben in den Editor aufzunehmen.

Als Nächstes verschiebe ich das Commit mit dem fix ( fe7f1e0) unter das Feature-Commit und wechsle pickzu fixup.

Wenn Sie den Editor schließen, wird das Update in das Feature-Commit gequetscht und mein Commit-Verlauf sieht gut und sauber aus!

Dies funktioniert gut, wenn alle Commits lokal sind. Wenn Sie jedoch versuchen, bereits auf die Fernbedienung übertragene Commits zu ändern, können Sie anderen Entwicklern, die denselben Zweig ausgecheckt haben, wirklich Probleme bereiten!

Geben Sie hier die Bildbeschreibung ein

br3nt
quelle
5
Müssen Sie die oberste auswählen und den Rest zerquetschen? Sie sollten Ihre Antwort bearbeiten, um zu erklären, wie Sie den interaktiven Rebase-Editor genauer verwenden
Kolob Canyon
2
Ja, lassen Sie pickin Zeile 1. Wenn Sie squashoder fixupfür das Commit in Zeile 1 wählen , zeigt git die Meldung "Fehler: Ohne vorheriges Commit kann nicht 'repariert' werden". Dann haben Sie die Möglichkeit, das Problem zu beheben: "Sie können dies mit 'git rebase --edit-todo' beheben und dann 'git rebase --continue' ausführen." oder Sie können einfach abbrechen und von vorne beginnen: "Oder Sie können die Rebase mit 'git rebase --abort' abbrechen."
Br3nt
56

Wenn Sie TortoiseGit verwenden, können Sie die Funktion Combine to one commit:

  1. Öffnen Sie das TortoiseGit-Kontextmenü
  2. Wählen Show Log
  3. Markieren Sie die entsprechenden Commits in der Protokollansicht
  4. Wählen Sie Combine to one commitaus dem Kontextmenü

Commits kombinieren

Diese Funktion führt automatisch alle erforderlichen Single-Git-Schritte aus. Leider nur für Windows verfügbar.

Matthias M.
quelle
Soweit mir bekannt ist, funktioniert dies bei Merge-Commits nicht.
Thorkil Holm-Jacobsen
1
Obwohl es von keinem anderen kommentiert wird, funktioniert dies sogar für Commits, die nicht am HEAD sind. Zum Beispiel war es mein Bedürfnis, einige WIP-Commits, die ich gemacht hatte, mit einer vernünftigeren Beschreibung zu quetschen, bevor ich sie drückte. Hat wunderbar funktioniert. Natürlich hoffe ich immer noch, dass ich lernen kann, wie man es durch Befehle macht.
Charles Roberto Canato
55

Dazu können Sie den folgenden git-Befehl verwenden.

 git rebase -i HEAD~n

n (= 4 hier) ist die Anzahl der letzten Festschreibungen. Dann haben Sie folgende Optionen:

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

Aktualisieren Sie wie unten beschrieben pickein Commit und squashdie anderen in das neueste,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

Für Details klicken Sie auf den Link

Jakir Hosen Khan
quelle
48

Basierend auf diesem Artikel fand ich diese Methode für meinen Anwendungsfall einfacher.

Mein 'dev'-Zweig lag um 96 Commits vor' origin / dev '(daher wurden diese Commits noch nicht auf die Fernbedienung übertragen).

Ich wollte diese Commits zu einem zusammenfassen, bevor ich die Änderung vorantreibe. Ich ziehe es vor, den Zweig auf den Status 'origin / dev' zurückzusetzen (dies lässt alle Änderungen von den 96 Commits nicht inszeniert) und dann die Änderungen sofort festzuschreiben:

git reset origin/dev
git add --all
git commit -m 'my commit message'
Trudolf
quelle
1
Genau das, was ich brauchte. Squash-Down-Commits aus meinem Feature-Zweig, und dann gebe ich diesen Commit in meinen Master.
David Victor
1
Dies quetscht frühere Commits nicht!
IgorGanapolsky
Könntest du das etwas weiter ausarbeiten @igorGanapolsky?
Trudolf
3
@trudolf Dies ist nicht wirklich Squashing (Auswahl einzelner Commits zum Squash). Dies bedeutet mehr, alle Ihre Änderungen auf einmal zu übernehmen.
IgorGanapolsky
15
Ja, daher werden alle Ihre Commits zu einem zusammengefasst. Herzliche Glückwünsche!
Trudolf
45

Führen Sie in der Verzweigung, in der Sie die Commits kombinieren möchten, Folgendes aus:

git rebase -i HEAD~(n number of commits back to review)

Beispiel:

git rebase -i HEAD~1

Dadurch wird der Texteditor geöffnet und Sie müssen die Auswahl vor jedem Commit durch "Squash" ersetzen, wenn diese Commits zusammengeführt werden sollen. Aus der Dokumentation:

p, pick = use commit

s, squash = Commit verwenden, aber mit dem vorherigen Commit verschmelzen

Wenn Sie beispielsweise alle Commits zu einem zusammenführen möchten, ist die Auswahl das erste Commit, das Sie vorgenommen haben, und alle zukünftigen Commits (unter dem ersten) sollten auf "Squash" gesetzt werden. Wenn Sie vim verwenden, verwenden Sie : x im Einfügemodus, um den Editor zu speichern und zu beenden.

Dann, um die Rebase fortzusetzen:

git rebase --continue

Weitere Informationen zu diesem und anderen Möglichkeiten zum Umschreiben Ihres Commit-Verlaufs finden Sie in diesem hilfreichen Beitrag

aabiro
quelle
2
Bitte erklären Sie auch, was das --continueund vim :xmacht.
not2qubit
Die Rebase erfolgt in Blöcken, während die Commits in Ihrem Zweig durchlaufen werden. Nachdem Sie git adddie richtige Konfiguration in Ihren Dateien vorgenommen haben git rebase --continue, wechseln Sie zum nächsten Commit und beginnen mit dem Zusammenführen. :xist ein Befehl, der die Änderungen der Datei bei Verwendung von vim speichert, siehe dies
aabiro
32

Die Antwort von Anomies ist gut, aber ich fühlte mich unsicher, deshalb habe ich beschlossen, ein paar Screenshots hinzuzufügen.

Schritt 0: Git-Protokoll

Sehen Sie, wo Sie sind git log. Am wichtigsten ist, dass Sie den Commit-Hash des ersten Commits finden, den Sie nicht quetschen möchten. Also nur die:

Geben Sie hier die Bildbeschreibung ein

Schritt 1: Git Rebase

Führen Sie git rebase -i [your hash]in meinem Fall aus:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Schritt 2: Wählen / quetschen Sie, was Sie wollen

In meinem Fall möchte ich alles auf dem Commit zerquetschen, was zum ersten Mal war. Die Bestellung erfolgt vom ersten bis zum letzten, also genau umgekehrt wie in git log. In meinem Fall möchte ich:

Geben Sie hier die Bildbeschreibung ein

Schritt 3: Nachricht (en) anpassen

Wenn Sie nur ein Commit ausgewählt und den Rest gequetscht haben, können Sie eine Commit-Nachricht anpassen:

Geben Sie hier die Bildbeschreibung ein

Das ist es. Sobald Sie dies ( :wq) gespeichert haben, sind Sie fertig. Schau es dir an mit git log.

Martin Thoma
quelle
2
wäre schön, das Endergebnis zu sehen, zBgit log
Timothy LJ Stewart
2
Nein Danke. Genau so habe ich meine Verpflichtungen erfüllt.
Axalix
@Axalix Hast du alle deine Zeilen entfernt? So verlieren Sie Ihre Commits.
a3y3
31

Vorgehensweise 1

1) Identifizieren Sie den Commit Short Hash

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Hier kann sogar git log --onelineauch verwendet werden, um kurzen Hash zu bekommen.

2) Wenn Sie die letzten beiden Commits quetschen (zusammenführen) möchten

# git rebase -i deab3412 

3) Dies öffnet einen nanoEditor zum Zusammenführen. Und es sieht aus wie unten

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Benennen Sie das vorher vorhandene Wort pickum . Nach dem Umbenennen sollte es wie folgt sein.squashabcd1234

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Speichern und schließen Sie nun den nanoEditor. Drücken Sie ctrl + ound drücken Sie Enterzu speichern. Drücken Sie dann ctrl + x, um den Editor zu verlassen.

6) Anschließend wird der nanoEditor erneut geöffnet, um Kommentare zu aktualisieren und gegebenenfalls zu aktualisieren.

7) Jetzt ist es erfolgreich gequetscht. Sie können es überprüfen, indem Sie die Protokolle überprüfen.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) Drücken Sie nun auf Repo. Hinweis zum Hinzufügen eines +Zeichens vor dem Filialnamen. Dies bedeutet erzwungenen Druck.

# git push origin +master

Hinweis: Dies basiert auf der Verwendung von git on ubuntushell. Wenn Sie ein anderes Betriebssystem ( Windowsoder Mac) verwenden, sind die obigen Befehle mit Ausnahme des Editors identisch. Möglicherweise erhalten Sie einen anderen Editor.

Vorgehensweise 2

  1. Fügen Sie zuerst die erforderlichen Dateien für das Festschreiben hinzu
git add <files>
  1. Dann Commit mit --fixupOption und das OLDCOMMITsollte sein, auf dem wir dieses Commit zusammenführen (Squash) müssen.
git commit --fixup=OLDCOMMIT

Dies schafft nun ein neues Commit über HEAD mit fixup1 <OLDCOMMIT_MSG>.

  1. Führen Sie dann den folgenden Befehl aus, um das neue Commit mit dem zusammenzuführen (zu quetschen) OLDCOMMIT.
git rebase --interactive --autosquash OLDCOMMIT^

Hier ^bedeutet das vorherige Commit zu OLDCOMMIT. Dieser rebaseBefehl öffnet ein interaktives Fenster in einem Editor (vim oder nano), in dem Sie nichts weiter tun müssen, nur das Speichern und Beenden ist ausreichend. Da die an diese Option übergebene Option das letzte Commit automatisch neben das alte Commit verschiebt und den Vorgang in fixup(entspricht Squash) ändert . Dann wird die Rebase fortgesetzt und beendet.

Vorgehensweise 3

  1. Wenn neue Änderungen am letzten Commit hinzugefügt werden müssen, --amendkönnen Mittel mit verwendet werden git-commit.
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

Hier werden --amenddie neuen Änderungen zum letzten Commit zusammengeführt cdababcdund eine neue Commit-ID generiert1d4ab2e1

Fazit

  • Der Vorteil des ersten Verfahrens besteht darin, mehrere Commits zu quetschen und neu zu ordnen. Dieses Verfahren wird jedoch schwierig, wenn wir einen Fix für ein sehr altes Commit zusammenführen müssen.
  • Das zweite Verfahren hilft also dabei, das Commit leicht mit einem sehr alten Commit zusammenzuführen.
  • Das dritte Verfahren ist in einem Fall hilfreich, um neue Änderungen für das letzte Festschreiben zu unterdrücken.
Rashok
quelle
Es werden nur die letzten beiden Commits aktualisiert, auch wenn ich auf eine Commit-ID zum 6. letzten Commit zurückgesetzt habe. Ich weiß nicht warum
Carlos Liu
Auch Sie können die Festschreibungsreihenfolge neu anordnen. Es funktioniert gut.
Rashok
29

So zerquetschen Sie die letzten 10 Commits zu einem einzigen Commit:

git reset --soft HEAD~10 && git commit -m "squashed commit"

Wenn Sie auch den Remote-Zweig mit dem Squashed Commit aktualisieren möchten:

git push -f
Ayan
quelle
--force ist gefährlich, wenn mehrere Personen an einem gemeinsam genutzten Zweig arbeiten, da die Remote-Aktualisierung blind mit Ihrer lokalen Kopie durchgeführt wird. --force-with-lease hätte besser sein können, da es sicherstellt, dass Remote keine Commits von anderen hat, seit Sie es das letzte Mal abgerufen haben.
Bharath
27

Wenn Sie sich in einem Remote-Zweig (aufgerufen feature-branch) befinden, der aus einem Golden Repository () geklont wurde golden_repo_name, können Sie Ihre Commits wie folgt in einen Zweig zerlegen :

  1. Kasse das goldene Repo

    git checkout golden_repo_name
    
  2. Erstellen Sie daraus einen neuen Zweig (goldenes Repo) wie folgt

    git checkout -b dev-branch
    
  3. Squash wird mit Ihrer lokalen Niederlassung zusammengeführt, die Sie bereits haben

    git merge --squash feature-branch
    
  4. Übernehmen Sie Ihre Änderungen (dies ist das einzige Commit, das in dev-branch ausgeführt wird).

    git commit -m "My feature complete"
    
  5. Schieben Sie den Zweig in Ihr lokales Repository

    git push origin dev-branch
    
Sandesh Kumar
quelle
Da ich gerade ~ 100 Commits gequetscht habe (zum Synchronisieren von SVN-Zweigen über Git-SVN), ist dies weitaus schneller als interaktives Rebasieren!
Salbei
1
Beim Lesen sehe ich @ Chris 'Kommentar, den ich früher gemacht habe (rebase --soft ...) - schade, dass der Stackoverflow die Antwort nicht mehr mit Hunderten von Upvotes an die Spitze setzt ...
Salbei
1
stimme dir zu @sage, lass uns hoffen, dass sie es irgendwann in der Zukunft tun
Sandesh Kumar
Das ist der richtige Weg. Der Rebase-Ansatz ist gut, sollte aber nur als letzter Ausweg für Squash verwendet werden.
Axalix
20

Was wirklich praktisch sein kann:
Finden Sie beispielsweise den Commit-Hash, auf den Sie Squash ausführen möchtend43e15 .

Jetzt benutzen

git reset d43e15
git commit -am 'new commit name'
Ariel Gabizon
quelle
2
Diese. Warum nutzen nicht mehr Leute das? Es ist viel schneller als das erneute Basieren und Quetschen einzelner Commits.
a3y3
17

Das ist super-duper kludgy, aber irgendwie cool, also werfe ich es einfach in den Ring:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

Übersetzung: Stellen Sie einen neuen "Editor" für git bereit, der, wenn der zu bearbeitende Dateiname lautet git-rebase-todo (die interaktive Rebase-Eingabeaufforderung), alle bis auf die erste "Auswahl" in "Squash" ändert und ansonsten vim erzeugt - so dass, wenn Sie dazu aufgefordert werden Um die Squashed Commit-Nachricht zu bearbeiten, erhalten Sie vim. (Und natürlich habe ich die letzten fünf Commits auf Branch Foo gequetscht, aber Sie können das ändern, wie Sie möchten.)

Ich würde wahrscheinlich tun, was Mark Longair vorgeschlagen hat.

Cascabel
quelle
7
+1: Das macht Spaß und ist lehrreich, da mir überhaupt nicht klar war, dass Sie etwas Komplexeres als den Namen eines Programms in die Umgebungsvariable GIT_EDITOR einfügen können.
Mark Longair
16

Wenn Sie jedes Commit in ein einzelnes Commit zerlegen möchten (z. B. wenn Sie ein Projekt zum ersten Mal öffentlich veröffentlichen), versuchen Sie Folgendes:

git checkout --orphan <new-branch>
git commit
William Denniss
quelle
15

2020 Einfache Lösung ohne Rebase:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2 bedeutet, dass die letzten beiden Commits gequetscht werden. Sie können es durch eine beliebige Nummer ersetzen

Xys
quelle
14

Ich denke, der einfachste Weg, dies zu tun, besteht darin, einen neuen Zweig aus dem Master zu machen und einen Merge-Quash des Feature-Zweigs durchzuführen.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Dann haben Sie alle Änderungen bereit, um festzuschreiben.

user1376350
quelle
12

Einfacher Einzeiler, der immer funktioniert, vorausgesetzt, Sie befinden sich derzeit in dem Zweig, den Sie quetschen möchten, Master ist der Zweig, aus dem er stammt, und das letzte Commit enthält die Commit-Nachricht und den Autor, den Sie verwenden möchten:

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
ColinM
quelle
4
Ich war absolut wütend vor Frustration über das Quetschen von Commits und wie dumm kompliziert es ist - benutze einfach die letzte Nachricht und quetsche sie alle zu einem Commit! Warum ist es so schwer ???? Dieser eine Liner macht das für mich. Vielen Dank aus tiefstem Herzen.
Locane
12

Wenn Sie beispielsweise die letzten 3 Commits für ein einzelnes Commit in einem Zweig (Remote-Repository) quetschen möchten, zum Beispiel: Bedingungen https://bitbucket.org

Was ich getan habe ist

  1. Git Reset - Soft Head ~ 3 &&
  2. Git Commit
  3. git push origin (branch_name) --force
Albert Ruelan
quelle
3
Seien
12

⚠️ WARNUNG: "Meine letzten X-Commits" sind möglicherweise nicht eindeutig.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

In dieser sehr verkürzten Historie des Repositorys https://github.com/fleetwood-mac/band-history haben Sie eine Pull-Anfrage geöffnet, um das Bill Clinton-Commit mit dem Original zusammenzuführen (MASTER ) Fleetwood Mac-Commit zusammenzuführen.

Sie haben eine Pull-Anfrage geöffnet und auf GitHub sehen Sie Folgendes:

Vier Commits:

  • Fügen Sie Danny Kirwan hinzu
  • Fügen Sie Christine Perfect hinzu
  • LA1974
  • Bill Clinton

Ich denke, dass es niemanden interessieren würde, den vollständigen Repository-Verlauf zu lesen. (Es gibt tatsächlich ein Repository, klicken Sie auf den Link oben!) Sie beschließen, diese Commits zu quetschen. Also gehst du und rennst git reset --soft HEAD~4 && git commit. Dann Siegit push --force es auf GitHub, um deine PR aufzuräumen.

Und was passiert? Sie haben gerade eine einzige Verpflichtung eingegangen, die von Fritz zu Bill Clinton führt. Weil Sie vergessen haben, dass Sie gestern an der Buckingham Nicks-Version dieses Projekts gearbeitet haben. Und git logstimmt nicht mit dem überein, was Sie auf GitHub sehen.

🐻 MORAL DER GESCHICHTE

  1. Finden Sie die genauen Dateien , die Sie erhalten möchten zu , undgit checkout sie
  2. Finden Sie das genaue vorherige Commit, das Sie in der Historie behalten möchten, und git reset --soft das
  3. Machen Sie eine git commit, die sich direkt von von nach nach oben verzieht
William Entriken
quelle
1
Dies ist zu 100% der einfachste Weg, dies zu tun. Wenn Ihre aktuelle HEAD ist der richtige Zustand Sie wollen, dann können Sie # 1 überspringen.
Stan
Dies ist der einzige mir bekannte Weg, der es ermöglicht, den ersten Commit-Verlauf neu zu schreiben.
Vadorequest
9

Wenn Sie sich nicht für die Commit-Nachrichten der Zwischen-Commits interessieren, können Sie diese verwenden

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend
Zsolt Z.
quelle
7

Wenn Sie mit GitLab arbeiten, können Sie einfach auf die Squash-Option in der Zusammenführungsanforderung klicken, wie unten gezeigt. Die Festschreibungsnachricht ist der Titel der Zusammenführungsanforderung.

Geben Sie hier die Bildbeschreibung ein

arush436
quelle
6
git rebase -i HEAD^^

wobei die Anzahl der ^ 's X ist

(In diesem Fall quetschen Sie die beiden letzten Commits.)

Yoaz Menda
quelle
6

Zusätzlich zu anderen hervorragenden Antworten möchte ich hinzufügen, wie git rebase -imich immer mit der Festschreibungsreihenfolge verwechselt wird - älter als neuere oder umgekehrt? Das ist also mein Workflow:

  1. git rebase -i HEAD~[N], wobei N die Anzahl der Commits ist, an denen ich teilnehmen möchte, beginnend mit dem letzten . Damitgit rebase -i HEAD~5 würde bedeuten , „quetscht die letzten 5 Commits in einen neuen“;
  2. Der Editor wird angezeigt und zeigt die Liste der Commits an, die ich zusammenführen möchte. Jetzt werden sie in umgekehrter Reihenfolge angezeigt : Das ältere Commit befindet sich oben. Markieren Sie alle Commits mit Ausnahme des ersten / älteren als "Squash" oder "s" : Sie werden als Ausgangspunkt verwendet. Speichern und schließen Sie den Editor.
  3. Der Editor wird erneut mit einer Standardmeldung für das neue Commit angezeigt: Ändern Sie sie nach Ihren Wünschen, speichern und schließen Sie sie. Squash abgeschlossen!

Quellen & zusätzliche Lesungen: # 1 , # 2 .

Ignorant
quelle
6

Was ist mit einer Antwort auf die Frage zu einem solchen Workflow?

  1. Viele lokale Commits, gemischt mit mehreren Zusammenführungen vom Master ,
  2. endlich ein Push zur Fernbedienung,
  3. PR und Merge TO Master vom Reviewer . (Ja, es wäre für den Entwickler einfacher, merge --squashnach der PR zu arbeiten, aber das Team dachte, dies würde den Prozess verlangsamen.)

Ich habe auf dieser Seite keinen solchen Workflow gesehen. (Das können meine Augen sein.) Wenn ich rebaserichtig verstehe , würden mehrere Zusammenführungen mehrere Konfliktlösungen erfordern . Ich möchte nicht einmal darüber nachdenken!

Das scheint also für uns zu funktionieren.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. viel lokal bearbeiten und festschreiben, Master regelmäßig zusammenführen
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // setzt alle Änderungen in die Bühne
  7. git commit 'one message to rule them all'
  8. git push
  9. Der Prüfer macht PR und verschmilzt zum Master.
Allenjom
quelle
Aus vielen Meinungen mag ich Ihren Ansatz. Es ist sehr bequem und schnell
Artem Solovev
5

Ich finde, eine allgemeinere Lösung besteht nicht darin, 'N'-Commits anzugeben, sondern die Verzweigungs- / Commit-ID, auf die Sie quetschen möchten. Dies ist weniger fehleranfällig als das Zählen der Festschreibungen bis zu einem bestimmten Festschreiben. Geben Sie das Tag einfach direkt an, oder wenn Sie wirklich zählen möchten, können Sie HEAD ~ N angeben.

In meinem Workflow starte ich einen Zweig, und mein erstes Commit für diesen Zweig fasst das Ziel zusammen (dh es ist normalerweise das, was ich als "endgültige" Nachricht für das Feature an das öffentliche Repository senden werde.) Wenn ich also fertig bin, alles Ich möchte tun ist git squash master zurück zur ersten Nachricht und dann bin ich bereit zu pushen.

Ich benutze den Alias:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

Dadurch wird der gequetschte Verlauf zuvor gelöscht. Dies gibt Ihnen die Möglichkeit, ihn wiederherzustellen, indem Sie eine alte Festschreibungs-ID von der Konsole abrufen, wenn Sie zurücksetzen möchten. (Solaris-Benutzer beachten, dass die GNU sed- -iOption verwendet wird. Mac- und Linux-Benutzer sollten damit einverstanden sein.)

Ethan
quelle
Ich habe den Alias ​​ausprobiert, bin mir aber nicht sicher, ob die sed-Ersetzungen Auswirkungen haben. Was sollten Sie tun?
Raine
Der erste Sed speichert den Verlauf einfach auf der Konsole. Das zweite sed ersetzt alle 'pick' durch 'f' (Fixup) und schreibt die Editor-Datei an Ort und Stelle neu (die Option -i). Der zweite erledigt also die ganze Arbeit.
Ethan
Sie haben Recht, das Zählen der N-Anzahl bestimmter Commits ist sehr fehleranfällig. Es hat mich mehrmals vermasselt und Stunden damit verschwendet, die Rebase rückgängig zu machen.
IgorGanapolsky
Hallo Ethan, ich würde gerne wissen, ob dieser Workflow mögliche Konflikte bei der Zusammenführung verbirgt. Überlegen Sie also bitte, ob Sie zwei Zweige Master und Slave haben. Wenn der Slave einen Konflikt mit dem Master hat und wir verwenden, git squash masterwenn wir auf dem Slave ausgecheckt sind. was wird es passieren Werden wir den Konflikt verbergen?
Sergio Bilello
@Sergio Dies ist ein Fall, in dem der Verlauf neu geschrieben wird. Daher treten wahrscheinlich Konflikte auf, wenn Sie bereits gepuschte Commits quetschen und dann versuchen, die gequetschte Version wieder zusammenzuführen / neu zu starten. (Einige triviale Fälle könnten damit durchkommen.)
Ethan
5

In Frage könnte es mehrdeutig sein, was mit "zuletzt" gemeint ist.

git log --graphgibt beispielsweise Folgendes aus (vereinfacht):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

Dann sind die letzten Commits nach Zeit H0, Merge, B0. Um sie zu quetschen, müssen Sie Ihren zusammengeführten Zweig auf Commit H1 neu gründen.

Das Problem ist, dass H0 H1 und H2 enthält (und im Allgemeinen mehr Commits vor dem Zusammenführen und nach dem Verzweigen), während B0 dies nicht tut. Sie müssen also mindestens Änderungen von H0, Merge, H1, H2, B0 verwalten.

Es ist möglich, Rebase zu verwenden, jedoch auf andere Weise als in anderen genannten Antworten:

rebase -i HEAD~2

Dies zeigt Ihnen Auswahlmöglichkeiten (wie in anderen Antworten erwähnt):

pick B1
pick B0
pick H0

Setzen Sie Squash statt Pick auf H0:

pick B1
pick B0
s H0

Nach dem Speichern und Beenden werden bei Rebase nach H1 nacheinander Commits angewendet. Das bedeutet, dass Sie aufgefordert werden, Konflikte erneut zu lösen (wobei HEAD zuerst H1 ist und dann Commits ansammelt, wenn sie angewendet werden).

Nach Abschluss der Rebase können Sie eine Nachricht für die gequetschten H0 und B0 auswählen:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

PS Wenn Sie nur einen Reset auf BO durchführen: (Dies wird beispielsweise reset --mixedhier unter https://stackoverflow.com/a/18690845/2405850 näher erläutert. )

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

Dann quetschen Sie sich in B0-Änderungen von H0, H1, H2 (der Verlust geht vollständig für Änderungen nach der Verzweigung und vor dem Zusammenführen über.

was auch immer
quelle
4

1) git reset --soft HEAD ~ n

n - Nummer des Commits, muss gequetscht werden

2) git commit -m "neue Festschreibungsnachricht"

3) git push origin branch_name --force

Poonkodi
quelle