Wie kann ich meine letzten X-Commits mithilfe von Git zu einem Commit zusammenfügen?
Verwenden Sie git rebase -i <after-this-commit>
und ersetzen Sie "pick" für das zweite und nachfolgende Commits durch "squash" oder "fixup", wie in the manual beschrieben.
In diesem Beispiel ist <after-this-commit>
entweder der SHA1-Hash oder die relative Position von HEAD des aktuellen Zweigs, von der aus Commits für den Rebase-Befehl analysiert werden. Wenn der Benutzer beispielsweise 5 Commits aus dem aktuellen HEAD in der Vergangenheit anzeigen möchte, lautet der Befehl git rebase -i HEAD~5
.
Sie können dies ziemlich einfach ohne git rebase
oder git merge --squash
tun. In diesem Beispiel werden die letzten 3 Commits gestrichen.
Wenn Sie die neue Festschreibungsnachricht von Grund auf schreiben möchten, reicht dies aus:
git reset --soft HEAD~3 &&
git commit
Wenn Sie die neue Commit-Nachricht mit einer Verkettung der vorhandenen Commit-Nachrichten bearbeiten möchten (z. B. ähnlich wie bei einer Pick/Squash/Squash /…/Squash git rebase -i
-Befehlsliste), müssen Sie diese Nachrichten extrahieren übergeben Sie sie an git commit
:
git reset --soft HEAD~3 &&
git commit --edit -m"$(git log --format=%B --reverse [email protected]{1})"
Bei beiden Methoden werden die letzten drei Commits auf dieselbe Art und Weise zu einem einzigen neuen Commit zusammengefasst. Beim Soft Reset wird HEAD nur auf den letzten Commit gesetzt, den Sie nicht quetschen möchten. Weder der Index noch der Arbeitsbaum werden durch den Soft-Reset berührt, sodass der Index für den neuen Commit im gewünschten Status verbleibt (d. H. Er enthält bereits alle Änderungen der Commits, die Sie "wegwerfen").
Sie können git merge --squash
dafür verwenden, was etwas eleganter als git rebase -i
ist. Angenommen, Sie sind auf Master und Sie möchten die letzten 12 Commits zu einem komprimieren.
WARNUNG: Stellen Sie zunächst sicher, dass Sie Ihre Arbeit festschreiben. Überprüfen Sie, ob git status
sauber ist (da git reset --hard
gestaffelte und nicht bereitgestellte Änderungen wegwirft).
Dann:
# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12
# [email protected]{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 [email protected]{1}
# Commit those squashed changes. The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit
Die Dokumentation für git merge
beschreibt die --squash
-Option detaillierter.
Update: Der einzige wirkliche Vorteil dieser Methode gegenüber dem einfacheren git reset --soft HEAD~12 && git commit
, den Chris Johnsen in seiner Antwort vorgeschlagen hat, besteht darin, dass Sie die Commit-Nachricht mit jeder Commit-Nachricht, die Sie zerquetschen, vorgefüllt erhalten.
Ich empfehle, wenn möglich git reset
Zu meiden - besonders für Git-Neulinge. Wenn Sie einen Prozess nicht wirklich automatisieren müssen, der auf einem Anzahl von Commits basiert, gibt es einen weniger exotischen Weg ...
git merge --squash (working branch name)
git commit
Die Festschreibungsnachricht wird basierend auf dem Squash vorab ausgefüllt.
Basierend auf Chris Johnsens Antwort ,
Füge 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 [email protected]{1})"; };f'
... oder mit der Windows-Eingabeaufforderung:
git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse [email protected]{1})\"; };f"
Ihr ~/.gitconfig
sollte jetzt diesen Alias enthalten:
[alias]
squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse [email protected]{1})\"; };f"
Verwendungszweck:
git squash N
... die automatisch die letzten N
-Commits zusammenfasst, einschließlich.
Hinweis: Die resultierende Commit-Nachricht ist eine Kombination aller gequetschten Commits in Reihenfolge. Wenn Sie damit nicht zufrieden sind, können Sie git commit --amend
jederzeit manuell ändern. (Oder bearbeiten Sie den Alias nach Ihrem Geschmack.)
Dank diesem praktischen Blog-Beitrag 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 Zweigstelle ohne Tracking-Informationen/Remote-Repo befinden.
Mit dem Befehl wird der interaktive Rebase-Editor geöffnet, in dem Sie wie üblich neu sortieren, squash, umformulieren usw. können.
Verwenden des interaktiven Basis-Editors:
Der interaktive Rebase-Editor zeigt die letzten drei Commits. Diese Einschränkung wurde von HEAD~3
beim Ausführen des Befehls git rebase -i HEAD~3
festgelegt.
Das letzte Commit, HEAD
, wird zuerst in Zeile 1 angezeigt. Die Zeilen, die mit einem #
beginnen, sind Kommentare/Dokumentationen.
Die angezeigte Dokumentation ist ziemlich klar. In einer beliebigen Zeile können Sie den Befehl von pick
in einen Befehl Ihrer Wahl ändern.
Ich ziehe es vor, den Befehl fixup
zu verwenden, da dies die Änderungen der Festschreibung in die Festschreibung in der obigen Zeile "drückt" und die Meldung der Festschreibung verwirft.
Da das Festschreiben in Zeile 1 HEAD
ist, würden Sie dies in den meisten Fällen als pick
belassen. Sie können squash
oder fixup
nicht verwenden, da es keine andere Festschreibung gibt, in die die Festschreibung eingedrückt wird.
Wenn Sie TortoiseGit verwenden, können Sie die Funktion Combine to one commit
verwenden:
Show Log
auswählenCombine to one commit
aus dem KontextmenüDiese Funktion führt automatisch alle erforderlichen Einzelschritte aus. Nur leider für Windows verfügbar.
Basierend auf diesem Artikel fand ich diese Methode für meine Anwendung einfacher.
Mein 'dev'-Zweig war 96' Commits vor 'Origin/dev' (also wurden diese Commits noch nicht an die Fernbedienung verschoben).
Ich wollte diese Commits zu einem komprimieren, bevor ich die Änderung treibe. Ich ziehe es vor, den Zweig auf den Status 'Origin/dev' zurückzusetzen (dadurch bleiben alle Änderungen der 96-Commits unstaged) und die Änderungen werden dann sofort festgeschrieben:
git reset Origin/dev
git add --all
git commit -m 'my commit message'
Dazu können Sie den folgenden git-Befehl verwenden.
git rebase -i HEAD~n
n (hier = 4) ist die Anzahl der letzten Commits. Dann haben Sie folgende Optionen,
pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....
Update wie unten,
p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....
Für Details klicken Sie auf den Link
Viel Glück!!
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 auch git log --oneline
verwendet werden, um einen kurzen Hash zu erhalten.
2) Wenn Sie die letzten beiden Commits quetschen möchten
# git rebase -i deab3412
3) Dies öffnet einen nano
-Editor zum Zusammenführen. Und es sieht unten aus
....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....
4) Benennen Sie das Wort pick
in squash
um, das vor abcd1234
vorhanden ist. Nach dem Umbenennen sollte es wie folgt aussehen.
....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....
5) Speichern und schließen Sie den nano
-Editor. Drücken Sie ctrl + o
und drücken Sie zum Speichern Enter
. Drücken Sie dann ctrl + x
, um den Editor zu verlassen.
6) Dann öffnet sich erneut der nano
-Editor, um die Kommentare zu aktualisieren, gegebenenfalls zu aktualisieren.
7) Jetzt ist es erfolgreich zusammengepresst. Sie können es durch Überprüfen der Protokolle überprüfen.
# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....
8) Jetzt zum Repo drücken. Beachten Sie, dass Sie vor dem Zweignamen ein +
-Zeichen hinzufügen müssen. Dies bedeutet erzwungenes Drücken.
# git Push Origin +master
Hinweis: Dies basiert auf der Verwendung von git in ubuntu
Shell. Wenn Sie ein anderes Betriebssystem verwenden (Windows
oder Mac
), sind die obigen Befehle mit Ausnahme des Editors identisch. Sie könnten einen anderen Editor bekommen.
Anomies antworten ist gut, aber ich fühlte mich darüber unsicher, also entschied ich mich, ein paar Screenshots hinzuzufügen.
Mit git log
sehen Sie, wo Sie sind. Am wichtigsten ist es, den Commit-Hash des ersten Commits zu finden, das Sie not nicht zerquetschen möchten. Also nur das:
Führen Sie git rebase -i [your hash]
aus, in meinem Fall:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
In meinem Fall möchte ich alles auf das Commit drücken, das zum ersten Mal gemacht wurde. Die Bestellung erfolgt vom ersten bis zum letzten, also genau wie in git log
. In meinem Fall möchte ich:
Wenn Sie nur ein Commit ausgewählt und den Rest unterdrückt haben, können Sie eine Commit-Nachricht anpassen:
Das ist es. Sobald Sie diese (:wq
) gespeichert haben, sind Sie fertig. Schau es dir mit git log
an.
Wenn Sie sich in einem entfernten Zweig (mit dem Namen feature-branch
) aus einem Golden Repository (golden_repo_name
) geklont haben, können Sie Ihre Commits zu einem komprimieren:
Überprüfen Sie das goldene Repo
git checkout golden_repo_name
Erstellen Sie einen neuen Zweig (Goldener Repo) wie folgt
git checkout -b dev-branch
Squash mischen Sie mit Ihrer lokalen Niederlassung, die Sie bereits haben
git merge --squash feature-branch
Übernehmen Sie Ihre Änderungen (dies ist das einzige Commit, das in dev-branch geht).
git commit -m "My feature complete"
Schieben Sie den Zweig in Ihr lokales Repository
git Push Origin dev-branch
Das ist super klasse, 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 zur Verfügung, der, wenn der zu bearbeitende Dateiname git-rebase-todo
(die Aufforderung zur interaktiven Neugestaltung) ist, alle außer dem ersten "Pick" in "Squash" ändert und ansonsten vim erzeugt - so, wenn Sie gerade sind Wenn Sie aufgefordert werden, die komprimierte Festschreibungsnachricht zu bearbeiten, erhalten Sie vim. (Und offensichtlich habe ich die letzten fünf Commits auf Branch Foo gequetscht, aber Sie könnten das ändern, wie Sie möchten.)
Ich würde wahrscheinlich tun, was Mark Longair vorschlug .
In der Branche, in der Sie die Commits zusammenfassen möchten, führen Sie Folgendes aus:
git rebase -i HEAD~(n number of commits back to review)
Daraufhin wird der Texteditor geöffnet und Sie müssen das "Pick" vor jedem Commit mit "Squash" ändern, wenn Sie möchten, dass diese Commits zusammengeführt werden. Wenn Sie beispielsweise alle Commits zu einem Commit zusammenführen möchten, ist der 'pick' der erste Commit, den Sie abgegeben haben, und alle zukünftigen Commits (unter dem ersten platziert) sollten auf 'squash' gesetzt werden. Wenn Sie vim verwenden, verwenden Sie : x im Einfügemodus, um den Editor zu speichern und zu beenden.
Dann geht es weiter:
git rebase --continue
Weitere Informationen zu diesem und anderen Möglichkeiten, Ihren Commit-Verlauf neu zu schreiben, finden Sie unter diesem hilfreichen Beitrag
Was kann wirklich bequem sein:
Finden Sie den Commit-Hash, den Sie über d43e15
ausdrücken möchten.
Jetzt benutzen
git reset d43e15
git commit -am 'new commit name'
Wenn Sie every commit zu einem einzigen Commit zusammenfassen möchten (z. B. wenn Sie ein Projekt zum ersten Mal öffentlich veröffentlichen), versuchen Sie Folgendes:
git checkout --Orphan <new-branch>
git commit
Ich denke, der einfachste Weg, dies zu tun, besteht darin, aus dem Master einen neuen Zweig zu erstellen und einen Zusammenführungsquerschnitt des Feature-Zweigs durchzuführen.
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
Dann haben Sie alle Änderungen zum Festschreiben bereit.
Um die letzten 10 Commits zu einem einzigen Commit zu quetschen:
git reset --soft HEAD~10 && git commit -m "squashed commit"
Wenn Sie auch den Remote-Zweig mit dem komprimierten Commit aktualisieren möchten:
git Push -f
git rebase -i HEAD^^
wo die Anzahl von ^ X ist
(in diesem Fall die letzten beiden Commits quetschen)
Eine generischere Lösung ist es, keine 'N'-Commits anzugeben, sondern eher die Branch-/Commit-ID, auf die Sie oben aufspringen möchten. Dies ist weniger fehleranfällig als das Zählen der Commits bis zu einem bestimmten Commit. Geben Sie einfach das Tag direkt an. 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 'letzte' Nachricht für das Feature an das öffentliche Repository schieben werde.) Wenn ich fertig bin, alles Ich möchte git squash master
zurück zur ersten Nachricht und dann bin ich bereit zu schieben.
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 komprimierte Verlauf ausgegeben, bevor er ausgeführt wird. Dies gibt Ihnen die Möglichkeit, sich zu erholen, indem Sie eine alte Festschreibungs-ID aus der Konsole holen, wenn Sie zurückkehren möchten. (Solaris-Benutzer beachten die Option GNU sed -i
. Mac- und Linux-Benutzer sollten damit zufrieden sein.)
In Frage könnte es mehrdeutig sein, was mit "letztem" gemeint ist.
zum Beispiel gibt git log --graph
folgendes (vereinfacht) aus:
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| |
* | commit H1
| |
* | commit H2
|/
|
Die letzten Commits nach Zeit sind H0, Merge, B0. Um sie zu quetschen, müssen Sie Ihren zusammengeführten Zweig beim Festschreiben H1 neu stützen.
Das Problem ist, dass H0 H1 und H2 enthält (und im Allgemeinen mehr Commits vor dem Zusammenführen und nach der Verzweigung), während B0 dies nicht tut. Sie müssen also mindestens Änderungen von H0, Zusammenführen, H1, H2, B0 verwalten.
Es ist möglich, rebase auf andere Weise als in den anderen genannten Antworten zu verwenden:
rebase -i HEAD~2
Dies zeigt Ihnen Wahlmö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 Sichern und Beenden wird rebase nach H1 Commits anwenden. Das bedeutet, dass Sie aufgefordert werden, Konflikte erneut aufzulösen (wobei HEAD zuerst H1 ist und dann Commits ansammelt, wenn sie angewendet werden).
Nachdem rebase beendet ist, können Sie eine Nachricht für H0 und B0 auswählen:
* commit squashed H0 and B0
|
* commit B1
|
* commit H1
|
* commit H2
|
P.S. Wenn Sie nur ein wenig auf BO zurücksetzen: (Verwenden Sie beispielsweise reset --mixed
, der hier näher erläutert wird: https://stackoverflow.com/a/18690845/2405850 ):
git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'
dann quetschen Sie in B0-Änderungen von H0, H1, H2 (der vollständige Verlust wird für Änderungen nach dem Verzweigen und vor dem Zusammenführen festgelegt.
Zusätzlich zu anderen hervorragenden Antworten möchte ich hinzufügen, dass git rebase -i
mich immer mit der Festschreibungsreihenfolge verwechselt - älterer zu neuerer oder umgekehrt? Das ist also mein Workflow:
git rebase -i HEAD~[N]
, wobei N die Anzahl der Commits ist, an denen ich teilnehmen möchte, beginnend mit dem neuesten. git rebase -i HEAD~5
würde also bedeuten, "die letzten 5 Commits in ein neues zu quetschen";Wenn Sie sich nicht für die Commit-Meldungen der dazwischen liegenden Commits interessieren, können Sie sie verwenden
git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend
(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 abgekürzten Geschichte des Repositorys https://github.com/fleetwood-mac/band-history haben Sie eine Pull-Anfrage geöffnet, um das Bill Clinton-Commit in das ursprüngliche (MASTER
) Fleetwood Mac-Commit einzubinden .
Sie haben eine Pull-Anfrage geöffnet und auf GitHub sehen Sie Folgendes:
Vier begeht:
Der Gedanke, dass niemand jemals den gesamten Repository-Verlauf lesen möchte. (Es gibt tatsächlich ein Repository, klicken Sie auf den Link oben!) Sie entscheiden, diese Commits zu quetschen. Also geh und git reset --soft HEAD~4 && git commit
laufen. Dann git Push --force
es auf GitHub, um deine PR aufzuräumen.
Und was passiert Sie haben gerade ein einzelnes Commit gemacht, das 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 log
stimmt nicht mit dem überein, was Sie auf GitHub sehen.
git checkout
siegit reset --soft
dasgit commit
, der sich direkt von von bis zu bis verziehtWie wäre es mit einer Antwort auf die Frage, die sich auf einen solchen Workflow bezieht?
merge --squash
nach der PR zu schreiben, aber das Team dachte, dies würde den Prozess verlangsamen.)Ich habe auf dieser Seite noch keinen solchen Workflow gesehen. (Das können meine Augen sein.) Wenn ich rebase
richtig verstehe, würden mehrere Zusammenführungen mehrere Konfliktlösungen erfordern. Ich möchte gar nicht darüber nachdenken!
Das scheint also für uns zu funktionieren.
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
// setzt alle Änderungen in die Bühnegit commit 'one message to rule them all'
git Push
Zunächst ermittle ich die Anzahl der Commits zwischen meinem Feature-Zweig und dem aktuellen Master-Zweig von
git checkout master
git rev-list master.. --count
Dann erstelle ich einen anderen Zweig, der auf meinem Feature-Zweig basiert. Behalte my-feature
Zweig unberührt.
Zuletzt renne ich
git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git Push Origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge
Hoffe es hilft, danke.
Einfacher One-Liner, der immer funktioniert, vorausgesetzt, Sie befinden sich derzeit in dem Zweig, in dem Sie Squash ausführen möchten. Master ist der Zweig, aus dem er stammt. Der letzte Commit enthält die Festschreibungsnachricht und den Autor, den Sie verwenden möchten:
git reset --soft $(git merge-base HEAD master) && git commit [email protected]{1}
Wenn Sie GitUp verwenden, wählen Sie das Commit aus, das Sie mit dem übergeordneten Element zusammenführen möchten, und drücken Sie S. Sie müssen es für jedes Commit einmal tun, aber es ist viel einfacher, als die korrekte Befehlszeilenbeschwörung aufzurufen. Vor allem, wenn Sie etwas nur gelegentlich machen.
wenn Sie beispielsweise die letzten drei Festschreibungen zu einer einzelnen Festschreibung in einem Zweig (Remote-Repository) zusammenfassen möchten, z. B .: https://bitbucket.org
Was ich getan habe ist
Fügen Sie diese bash-Funktion einfach zu Ihrer bash-Datei.
# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
if [ -z "${1}" -o -z "${2}" ]; then
echo "Usage: \`squash X COMMIT_MSG\`"
echo "X= Number of last commits."
echo "COMMIT_MSG= New commit msg."
return 1
fi
git reset --soft HEAD~"$1"
git add . && git ci -m "$2" # With 100 emoji
git Push --force
}
Dann lauf einfach
squash X 'New Commit Message'
Und du bist fertig.