Kann ich ein Git-Commit löschen, aber die Änderungen behalten?

1056

In einem meiner Entwicklungszweige habe ich einige Änderungen an meiner Codebasis vorgenommen. Bevor ich die Funktionen, an denen ich arbeitete, fertigstellen konnte, musste ich meinen aktuellen Zweig auf Master umstellen, um einige Funktionen zu testen. Durch die Verwendung eines "Git Checkout-Masters" wurden die Änderungen, die ich auch in meinem Entwicklungszweig vorgenommen habe, beibehalten, wodurch einige der Funktionen im Master beeinträchtigt wurden. Also habe ich die Änderungen in meinem Entwicklungszweig mit einer Festschreibungsnachricht "temporäres Festschreiben" festgeschrieben und dann den Master für die Demo ausgecheckt.

Nachdem ich mit der Demo fertig bin und wieder an meinem Entwicklungszweig arbeite, möchte ich das "temporäre Commit" entfernen, das ich vorgenommen habe, während ich die vorgenommenen Änderungen beibehalten habe. Ist das möglich?

Tanookiben
quelle
65
Nächstes Mal:git stash
Matt Ball
51
@ MattBall, nicht unbedingt. Während git stashein gutes Werkzeug ist „work in progress“ Wegwerf-Commits sind ein ganz legitimes Gerät auch.
Kostix
3
Dies ist eine großartige Ressourcenstraße von Github: Wie man (fast) alles mit Git rückgängig macht
jasonleonhard
9
@MattBall @kostix Ja, Stash eignet sich besonders schlecht für "Langzeit" -Stashing, da es sich um einen globalen Stack für das gesamte Repo handelt. Ich möchte in der Lage sein, Änderungen in einem Zweig zu speichern und dann zu anderen Zweigen zu gehen, alles andere zu tun und Tage später wiederzukommen, ohne mir Sorgen zu machen, dass ich git stashin der Zwischenzeit einen anderen Zweig verwendet habe.
Alec
4
Eine erwähnenswerte Sache stashist, dass es vollständig lokal ist und anfällig für Codeverlust durch Repo-Löschung oder Neuerstellung oder Hardwarefehler oder -verlust ist. IMO, es sollte wirklich nur für sehr kurzfristige WIP verwendet werden. Lieben Sie ein WIP-Commit, bevor Sie in den Urlaub fahren: P .. nennen Sie es ein Brain Dump-Commit!
23οδεMεδιϲ

Antworten:

1617

So einfach ist das:

git reset HEAD^

Hinweis: Einige Shells werden ^als Sonderzeichen behandelt (z. B. einige Windows-Shells oder ZSH mit aktiviertem Globbing ). "HEAD^"In diesen Fällen müssen Sie möglicherweise Anführungszeichen setzen .

git resetohne a --hardoder --softverschiebt Ihren HEADto-Punkt auf das angegebene Commit, ohne Dateien zu ändern. HEAD^bezieht sich auf das (erste) übergeordnete Commit Ihres aktuellen Commits, in Ihrem Fall das Commit vor dem temporären.

Beachten Sie, dass eine andere Option darin besteht, wie gewohnt fortzufahren und stattdessen am nächsten Festschreibungspunkt auszuführen:

git commit --amend [-m … etc]

Dadurch wird stattdessen das letzte Commit bearbeitet , mit dem gleichen Effekt wie oben.

Beachten Sie, dass dies (wie bei fast jeder Git-Antwort) Probleme verursachen kann, wenn Sie das schlechte Commit bereits an einen Ort verschoben haben, an dem es möglicherweise von einer anderen Person abgerufen wurde. Versuche das zu vermeiden

Gareth
quelle
47
Dies ist bei weitem das einfachste, aber wenn Sie Ihr Commit bereits auf eine Fernbedienung übertragen haben und jemand anderes es gezogen hat, würde ich sehr zögern, etwas anderes zu tun, als mich zu entschuldigen.
atw13
11
@sicophrenic, verpassen Sie nicht die Gelegenheit, bei dieser Gelegenheit "Reset Demystified" zu lesen .
Kostix
33
Ich bekomme More?danach. Was auch immer ich an dieser Eingabeaufforderung fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
eingebe
50
@DaAwesomeP klingt so, als würden Sie eine Shell verwenden, die ^als Sonderzeichen behandelt wird . Sie können entweder die Referenz zitieren "HEAD^"oder die alternative Syntax ohne HEAD~1Anführungszeichen verwenden
Gareth
8
Arbeitete für mich, musste aber Charakter entkommengit reset HEAD\^
Cevaris
188

Es gibt zwei Möglichkeiten, damit umzugehen. Was einfacher ist, hängt von Ihrer Situation ab

Zurücksetzen

Wenn das Commit, das Sie entfernen möchten, das letzte Commit war und Sie keine zusätzliche Arbeit geleistet haben, können Sie es einfach verwenden git-reset

git reset HEAD^

Bringt Ihren Zweig kurz vor Ihrem aktuellen HEAD zum Commit zurück. Die Dateien in Ihrem Arbeitsbaum werden jedoch nicht geändert. Infolgedessen werden die Änderungen in diesem Commit als geändert angezeigt - es ist wie ein Befehl zum Aufheben der Festschreibung. Tatsächlich habe ich einen Alias, um genau das zu tun.

git config --global alias.uncommit 'reset HEAD^'

Dann können Sie git uncommitin Zukunft nur noch ein Commit sichern.

Quetschen

Das Quetschen eines Commits bedeutet, zwei oder mehr Commits zu einem zu kombinieren. Ich mache das ziemlich oft. In Ihrem Fall haben Sie eine halbfertige Funktion festgeschrieben, und dann würden Sie sie beenden und erneut mit der richtigen, dauerhaften Festschreibungsnachricht festschreiben.

git rebase -i <ref>

Ich sage oben, weil ich klarstellen möchte, dass dies eine beliebige Anzahl von Commits zurück sein kann. Führen git logSie das Commit aus, das Sie entfernen möchten, kopieren Sie das SHA1 und verwenden Sie es anstelle von <ref>. Git versetzt Sie in den interaktiven Rebase-Modus. Es werden alle Commits zwischen Ihrem aktuellen Status und dem angezeigt, was Sie anstelle von <ref>. Wenn <ref>es also 10 Commits her ist, werden Ihnen alle 10 Commits angezeigt.

Vor jedem Commit steht das Wort pick. Suchen Sie das Commit, das Sie entfernen möchten, und ändern Sie es von pickin fixupoder squash. Durch fixupeinfaches Verwerfen wird eine Nachricht festgeschrieben und die Änderungen werden mit ihrem unmittelbaren Vorgänger in der Liste zusammengeführt. Das squashSchlüsselwort macht dasselbe, ermöglicht es Ihnen jedoch, die Festschreibungsnachricht des neu kombinierten Festschreibens zu bearbeiten.

Beachten Sie, dass die Festschreibungen in der Reihenfolge erneut festgeschrieben werden, in der sie in der Liste angezeigt werden, wenn Sie den Editor verlassen. Wenn Sie also ein temporäres Commit durchgeführt, dann andere Arbeiten an demselben Zweig ausgeführt und die Funktion in einem späteren Commit abgeschlossen haben, können Sie die Commits mithilfe von rebase neu sortieren und quetschen.

WARNUNG:

Durch erneutes Basieren wird der Verlauf geändert. Führen Sie dies NICHT für Commits durch, die Sie bereits mit anderen Entwicklern geteilt haben.

Verstauen

Um dieses Problem in Zukunft zu vermeiden, sollten Sie in Betracht ziehen, git stashnicht festgeschriebene Arbeiten vorübergehend zu speichern.

git stash save 'some message'

Dadurch werden Ihre aktuellen Änderungen seitlich in Ihrer Stash-Liste gespeichert. Oben ist die expliziteste Version des Befehls stash aufgeführt, die einen Kommentar enthält, der beschreibt, was Sie verstecken. Sie können auch einfach ausführen git stashund nichts anderes, aber es wird keine Nachricht gespeichert.

Sie können Ihre Vorratsliste durchsuchen mit ...

git stash list

Dies zeigt Ihnen alle Ihre Verstecke, welche Zweige sie bearbeitet haben, sowie die Nachricht und am Anfang jeder Zeile und die Kennung für diesen Versteck, der so aussieht, stash@{#}wobei # seine Position im Array der Verstecke ist.

Um einen Stash wiederherzustellen (was in jedem Zweig möglich ist, unabhängig davon, wo der Stash ursprünglich erstellt wurde), führen Sie einfach ...

git stash apply stash@{#}

Wieder gibt es # die Position im Array der Stashes. Wenn sich der Speicher, den Sie wiederherstellen möchten, in der 0Position befindet, dh wenn es sich um den letzten Speicher handelt. Dann können Sie den Befehl einfach ausführen, ohne die Stash-Position anzugeben. Git geht davon aus, dass Sie die letzte meinen : git stash apply.

Wenn ich zum Beispiel am falschen Zweig arbeite, kann ich die folgende Befehlsfolge ausführen.

git stash
git checkout <correct_branch>
git stash apply

In Ihrem Fall haben Sie sich ein bisschen mehr um Zweige bewegt, aber die gleiche Idee gilt immer noch.

Hoffe das hilft.

eddiemoya
quelle
6
git config --global alias.uncommit reset HEAD^Nur Aliase, die nicht zum Zurücksetzen verpflichtet sind. Stattdessen tungit config --global alias.uncommit 'reset HEAD^'
mernst
3
Beachten Sie, dass Sie ^^ anstelle von ^ verwenden müssen, wenn Sie in einer Windows-Eingabeaufforderung verwenden.
Pramod BR
106

Ich denke du suchst das

git reset --soft HEAD~1

Das letzte Commit wird rückgängig gemacht, während die in diesem Commit vorgenommenen Änderungen für die Bereitstellung beibehalten werden.

Sudhanshu Jain
quelle
7
Vielen Dank. Das hat bei mir funktioniert. Wenn Sie git reset HEAD^Windows aufrufen, wird nur "Mehr?" - was auch immer das bedeutet
Tyron
12
@ Tyron ^ist ein Escape-Zeichen in DOS. Wenn es mit einer neuen Zeile gekoppelt ist, dient es als Fortsetzung der Eingabeaufforderung für den vorhergehenden Befehl. Die Eingabe git reset HEAD^^sollte unter Windows funktionieren.
Trk
40

Ja, Sie können Ihr Commit löschen, ohne die Änderungen zu löschen: git reset @ ~

DURGESH
quelle
7
Das ist wirklich was ich will, und es wird meiner Meinung nach die akzeptierte Antwort sein, vielen Dank!
Carlos Liu
2
Interessante, kompakte Syntax, die ich noch nie gesehen oder verwendet habe. Wie unterscheidet sich das von git reset --softoder git reset --keep?
user776686
18

Sie suchen entweder git reset HEAD^ --softoder git reset HEAD^ --mixed.

Es gibt 3 Modi für den Rücksetzbefehl, wie in den Dokumenten angegeben :

git reset HEAD^ --soft

rückgängig machen git commit. Änderungen sind noch im Arbeitsbaum (dem Projektordner) + im Index (--cached) vorhanden.

git reset HEAD^ --mixed

rückgängig machen git commit+ git add. Im Arbeitsbaum sind noch Änderungen vorhanden

git reset HEAD^ --hard

Als hätten Sie diese Änderungen nie an der Codebasis vorgenommen. Änderungen wurden vom Arbeitsbaum entfernt.

Bar Horing
quelle
9

Für diejenigen, die zsh verwenden, müssen Sie Folgendes verwenden:

git reset --soft HEAD\^

Hier erklärt: https://github.com/robbyrussell/oh-my-zsh/issues/449

Für den Fall, dass die URL tot wird, ist der wichtige Teil:

Entkomme dem ^ in deinem Befehl

Alternativ können Sie HEAD ~ verwenden, damit Sie nicht jedes Mal entkommen müssen.

Greg Hilston
quelle
15
Ich kann mich nie an diesen Befehl erinnern und muss ihn googeln und meine eigene Antwort finden hahahaha
Greg Hilston
2
git reset HEAD^arbeitet in zsh für mich, kann behoben worden sein.
Ben Kolya Mansley
@ BenKolyaMansley Welche Version von zsh verwenden Sie?
Greg Hilston
Ich benutzezsh 5.3 (x86_64-apple-darwin18.0)
Ben Kolya Mansley
7

In meinem Fall habe ich schon zum Repo gedrängt. Autsch!

Sie können ein bestimmtes Commit zurücksetzen, während Sie die Änderungen in Ihren lokalen Dateien beibehalten, indem Sie Folgendes tun:

git revert -n <sha>

Auf diese Weise konnte ich die Änderungen beibehalten, die ich benötigte, und ein bereits verschobenes Commit rückgängig machen.

Wim Feijen
quelle
In meinem Fall musste ich etwas zurücksetzen, das ich bereits in das Remote-Repository verschoben hatte. Noch autsch! Der beste Weg war git revert bad-commit-sha, dann git revert -n revert-commit-just-created-shaund dann von dort zu reparieren. Du hast mich auf halbem Weg. Vielen Dank!
TinkerTenorSoftwareGuy
Dies scheint das Gegenteil zu tun, nein? Es werden neue Änderungen erstellt, die einem Zurücksetzen der im ausgewählten Commit vorgenommenen Änderungen entsprechen. Wenn Sie diese neuen Änderungen übernehmen, haben Sie die Arbeit, die Sie tatsächlich behalten wollten, rückgängig gemacht.
svaens
3

Wenn Sie git 2.9 (genau 2.9.2.windows.1) verwenden, werden Sie git reset HEAD^nach mehr gefragt . Ich bin mir nicht sicher, was hier erwartet wird. Bitte sehen Sie unten Screenshot

Geben Sie hier die Bildbeschreibung ein

Es wurde eine andere Lösung gefunden, git reset HEAD~#numberOfCommitsmit der wir die Anzahl der lokalen Commits auswählen können, die Sie zurücksetzen möchten, indem Sie Ihre Änderungen beibehalten. Daher haben wir die Möglichkeit, alle lokalen Commits sowie eine begrenzte Anzahl lokaler Commits wegzuwerfen.

Siehe folgende Screenshots, die git reset HEAD~1in Aktion gezeigt werden: Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

nkharche
quelle
Sie müssen wahrscheinlich dem Zeichen ^ entkommen - versuchen Sie, git reset "HEAD ^" oder git reset HEAD \ ^
Mark Fisher
Hinzu kommt, dass ein Git-Reset HEAD ^^ als einzelne ^ als neue Zeile behandelt wird.
John Oss
2

Ein weiterer Weg, es zu tun.

Fügen Sie ein Commit über dem temporären Commit hinzu und führen Sie dann Folgendes aus:

git rebase -i

Um zwei Commits zu einem zusammenzuführen (Befehl öffnet eine Textdatei mit expliziten Anweisungen, bearbeiten Sie sie).

Ruslan Osipov
quelle
2
Technisch korrekt, aber bei weitem nicht so elegant wie einfach git reset HEAD^. Git Rebase hat hier viel Raum für Fehler.
TheBuzzSaw
0

2020 Einfacher Weg:

git reset <commit_hash>

(Der Commit-Hash des letzten Commits, den Sie behalten möchten).

Wenn das Commit verschoben wurde, können Sie Folgendes tun:

git push -f

Sie behalten die jetzt nicht festgeschriebenen Änderungen lokal bei

Xys
quelle