Teil eines Commits mit git zurücksetzen

142

Ich möchte ein bestimmtes Commit in Git zurücksetzen. Leider verwendet unsere Organisation immer noch CVS als Standard. Wenn ich mich also wieder für CVS engagiere, werden mehrere Git-Commits zu einem zusammengefasst. In diesem Fall würde ich gerne das ursprüngliche Git-Commit herausgreifen, aber das ist unmöglich.

Gibt es einen ähnlichen Ansatz git add --patch, mit dem ich Unterschiede selektiv bearbeiten kann, um zu entscheiden, welche Teile eines Commits zurückgesetzt werden sollen?

Skiphoppy
quelle
Weitere Lösungen hier , aber Konzentration auf die Beschränkung der teilweisen Wiederherstellung auf bestimmte Dateien.
Ntc2

Antworten:

225

Verwenden Sie die Option --no-commit( -n) git revert, um die Änderungen aufzuheben, und verwenden Sie dann git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Hinweis: Die Dateien, die Sie mit git add --patch hinzufügen, sind die Dateien, die Sie zurücksetzen möchten, nicht die Dateien, die Sie behalten möchten.

Mipadi
quelle
13
Es kann sich lohnen, den letzten erforderlichen Befehl für diejenigen hinzuzufügen, die mit git nicht so vertraut sind: nach git reset --harddem Festschreiben die anderen Änderungen zu verwerfen, die Sie nicht zurücksetzen wollten.
tremby
15
git reset --hardist gefährlich für Neulinge, da es gewünschte Änderungen verlieren könnte. Statt gewöhnen git status, diese Hinweise für git checkout -- FILE..zu revert Dinge mehr sicher.
Tino
Was für eine klaffende Unterlassung git; git revertsollte nur ein --patchArgument nehmen.
Kaz
@Kaz: git revertwird zum Zurücksetzen ganzer Commits verwendet. Sie können git checkout -pinteraktiv Bits zum Zurücksetzen auswählen.
Mipadi
1
Ich möchte auch das (vielleicht) Offensichtliche hinzufügen, das zuerst Ihre Arbeit rettet . Entweder commitzuerst oder stashdann versuchen revert.
Felipe Alvarez
39

Ich habe Folgendes erfolgreich verwendet.

Setzen Sie zuerst das vollständige Commit zurück (setzt es in den Index), aber schreiben Sie es nicht fest.

git revert -n <sha1>  # -n is short for --no-commit

Entfernen Sie dann interaktiv die rückgängig gemachten GUTEN Änderungen aus dem Index

git reset -p          # -p is short for --patch  

Übernehmen Sie dann den umgekehrten Unterschied der fehlerhaften Änderungen

git commit -m "Partially revert <sha1>..."

Schließlich befinden sich die rückgängig gemachten GUTEN Änderungen (die durch den Befehl zum Zurücksetzen nicht bereitgestellt wurden) noch im Arbeitsbaum. Sie müssen aufgeräumt werden. Wenn im Arbeitsbaum keine weiteren nicht festgeschriebenen Änderungen verbleiben, kann dies von durchgeführt werden

git reset --hard
user1338062
quelle
5
Ist dies nicht eine überlegene Alternative zu der akzeptierten Antwort (die verwendet wird reset HEAD .), da keine endgültige Bereinigung des Arbeitsverzeichnisses erforderlich ist?
Steven Lu
2
Diese Antwort ist überlegen, weil sie reset -pkürzer ist als reset HEADgefolgt von add -p. Die Bereinigung ist jedoch weiterhin erforderlich, da sich die zurückgesetzten "guten" Hunks nach dem Festschreiben noch im Arbeitsverzeichnis befinden.
Chiel ten Brinke
Diese Antwort ist nicht überlegen, da das interaktive Entfernen der gewünschten Änderungen oft verwirrend und fehleranfällig ist - insbesondere, wenn eine dieser Änderungen bearbeitet werden muss.
Kaz
5

Persönlich bevorzuge ich diese Version, die die automatisch generierte Festschreibungsnachricht wiederverwendet und dem Benutzer die Möglichkeit gibt, das Wort "Teilweise" vor dem endgültigen Festschreiben zu bearbeiten und einzufügen.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .
jeffcook2150
quelle
4

Lösung:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.
Krzysztof Kaczmarski
quelle
Es würde den Menschen helfen, wenn Sie sagen würden, dass es sich von der akzeptierten Lösung unterscheidet
CharlesB
1
@Krzysztof Warum ist das Auschecken am Ende wichtig und warum unterscheidet sich diese Lösung von der von user1338062?
Martin
3

Eine andere Alternative (wenn Ihre aktuelle Version einer Datei nicht zu weit von der Version entfernt ist, die Sie zurücksetzen möchten ) besteht darin, den Hash des Commits unmittelbar vor dem abzurufen, von dem Sie teilweise zurücksetzen möchten (von git log). Dann wird Ihr Befehl:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

Dadurch werden die Dateien in Ihrem Arbeitsbaum geändert, es werden jedoch keine Commits erstellt. Wenn Sie also wirklich Probleme haben, können Sie einfach von vorne beginnen git reset --hard -- file/you/want/to/fix.ext.

Walf
quelle
1

Sie können git-revert -n verwenden und dann add --patch verwenden, um Hunks auszuwählen.

William Pursell
quelle