Ich habe gerade gelesen, wie ich eine einzelne Datei in einem früheren Commit in Git geändert habe, aber leider "ordnet" die akzeptierte Lösung die Commits neu, was nicht das ist, was ich will. Hier ist meine Frage:
Hin und wieder bemerke ich einen Fehler in meinem Code, während ich an einer (nicht verwandten) Funktion arbeite. Ein kurzer git blame
Blick zeigt dann, dass der Fehler vor einigen Commits eingeführt wurde (ich begebe ziemlich viel, daher ist es normalerweise nicht das letzte Commit, das den Fehler verursacht hat). An diesem Punkt mache ich normalerweise Folgendes:
git stash # temporarily put my work aside
git rebase -i <bad_commit>~1 # rebase one step before the bad commit
# mark broken commit for editing
vim <affected_sources> # fix the bug
git add <affected_sources> # stage fixes
git commit -C <bad_commit> # commit fixes using same log message as before
git rebase --continue # base all later changes onto this
Dies passiert jedoch so oft, dass die obige Sequenz nervt. Besonders die "interaktive Rebase" ist langweilig. Gibt es eine Verknüpfung zu der obigen Sequenz, mit der ich ein beliebiges Commit in der Vergangenheit mit den inszenierten Änderungen ändern kann? Ich bin mir vollkommen bewusst, dass dies die Geschichte verändert, aber ich mache so oft Fehler, dass ich wirklich gerne so etwas hätte
vim <affected_sources> # fix bug
git add -p <affected_sources> # Mark my 'fixup' hungs for staging
git fixup <bad_commit> # amend the specified commit with staged changes,
# rebase any successors of bad commit on rewritten
# commit.
Vielleicht ein intelligentes Skript, das Commits mit Sanitärwerkzeugen oder so umschreiben kann?
rebase -i
?rebase --onto tmp bad-commit master
. Wie geschrieben wird versucht, das fehlerhafte Festschreiben auf den festen Festschreibungsstatus anzuwenden.Antworten:
AKTUALISIERTE ANTWORT
Vor einiger Zeit wurde ein neues
--fixup
Argument hinzugefügt, mitgit commit
dem ein Commit mit einer geeigneten Protokollnachricht erstellt werden kanngit rebase --interactive --autosquash
. Der einfachste Weg, um ein früheres Commit zu reparieren, ist jetzt:URSPRÜNGLICHE ANTWORT
Hier ist ein kleines Python-Skript, das ich vor einiger Zeit geschrieben habe und das diese
git fixup
Logik implementiert, auf die ich in meiner ursprünglichen Frage gehofft hatte. Das Skript geht davon aus, dass Sie einige Änderungen vorgenommen haben, und wendet diese Änderungen dann auf das angegebene Commit an.HINWEIS : Dieses Skript ist Windows-spezifisch. Es sucht
git.exe
und setzt dieGIT_EDITOR
Umgebungsvariable mitset
. Passen Sie dies nach Bedarf für andere Betriebssysteme an.Mit diesem Skript kann ich genau den Workflow implementieren, bei dem defekte Quellen behoben, Bühnenkorrekturen durchgeführt, Git-Korrekturen ausgeführt wurden, nach dem ich gefragt habe:
quelle
git stash
undgit stash pop
um Ihre Rebase verwenden, um kein sauberes Arbeitsverzeichnis mehr zu benötigengit stash
undgit stash pop
: Du hast Recht, aber leidergit stash
ist viel langsamer auf Windows , als es unter Linux oder OS / X ist. Da mein Arbeitsverzeichnis normalerweise sauber ist, habe ich diesen Schritt weggelassen, um den Befehl nicht zu verlangsamen.git rebase -i --fixup
, und es wurde vom festgelegten Commit als Ausgangspunkt neu erstellt, sodass das sha-Argument in meinem Fall nicht benötigt wurde.git config --global rebase.autosquash true
Was ich tue ist:
Ihr Editor wird mit einer Liste der letzten 5 Commits geöffnet, in die Sie sich einmischen können. Veränderung:
...zu:
Speichern und beenden Sie Ihren Editor, und das Update wird wieder in das Commit eingefügt, zu dem es gehört.
Nachdem Sie dies einige Male getan haben, werden Sie es in Sekundenschnelle im Schlaf tun. Interaktives Rebasing ist das Feature, das mich bei Git wirklich verkauft hat. Es ist unglaublich nützlich für dieses und mehr ...
quelle
--autosquash
Schalter für interessiert seingit rebase
, der die Schritte im Editor automatisch für Sie neu anordnet . In meiner Antwort finden Sie ein Skript, das dies nutzt, um einengit fixup
Befehl zu implementieren .Ein bisschen spät zur Party, aber hier ist eine Lösung, die so funktioniert, wie es sich der Autor vorgestellt hat.
Fügen Sie dies Ihrer .gitconfig hinzu:
Anwendungsbeispiel:
Wenn Sie jedoch nicht bereitgestellte Änderungen vorgenommen haben, müssen Sie diese vor der erneuten Basis speichern.
Sie können den Alias so ändern, dass er automatisch gespeichert wird, anstatt eine Warnung zu geben. Wenn das Update jedoch nicht sauber angewendet wird, müssen Sie den Stash nach dem Beheben der Konflikte manuell öffnen. Das manuelle Speichern und Poppen erscheint konsistenter und weniger verwirrend.
quelle
git fixup HEAD
ich einen Alias für erstellt. Ich könnte auch eine Änderung dafür verwenden, nehme ich an.amend = commit --amend --reuse-message=HEAD
Dann können Sie einfach den Editor für die Festschreibungsnachricht eingebengit amend
odergit amend -a
überspringen.So beheben Sie ein Commit:
Dabei ist a0b1c2d3 ein Commit, das Sie korrigieren möchten, und wobei 2 die Anzahl der eingefügten Commits +1 ist, die Sie ändern möchten.
Hinweis: git rebase --autosquash ohne -i hat nicht funktioniert, aber mit -i hat funktioniert, was seltsam ist.
quelle
--autosquash
ohne-i
funktioniert immer noch nicht.EDITOR=true git rebase --autosquash -i
UPDATE: Eine übersichtlichere Version des Skripts finden Sie jetzt hier: https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup .
Ich habe nach etwas Ähnlichem gesucht. Dieses Python-Skript scheint jedoch zu kompliziert zu sein, daher habe ich meine eigene Lösung zusammengestellt:
Erstens sehen meine Git-Aliase so aus (von hier entlehnt ):
Jetzt wird die Bash-Funktion ganz einfach:
Dieser Code führt zuerst alle aktuellen Änderungen durch (Sie können diesen Teil entfernen, wenn Sie die Dateien selbst bereitstellen möchten). Anschließend wird das Fixup erstellt (Squash kann auch verwendet werden, wenn Sie dies benötigen). Danach startet es eine interaktive Rebase mit dem
--autosquash
Flag auf dem übergeordneten Commit , das Sie als Argument . Dadurch wird Ihr konfigurierter Texteditor geöffnet, sodass Sie überprüfen können, ob alles wie erwartet ist. Durch einfaches Schließen des Editors wird der Vorgang abgeschlossen.Der
if [[ "$1" == HEAD* ]]
Teil (von hier ausgeliehen ) wird verwendet. Wenn Sie beispielsweise HEAD ~ 2 als Commit-Referenz (das Commit, mit dem Sie aktuelle Änderungen korrigieren möchten) verwenden, wird der HEAD verschoben, nachdem das Fixup-Commit erstellt wurde und Sie müssten HEAD ~ 3 verwenden, um auf dasselbe Commit zu verweisen.quelle
Sie können die interaktive Phase vermeiden, indem Sie einen "Null" -Editor verwenden:
Dies wird
/bin/true
anstelle von als Editor verwendet/usr/bin/vim
. Es akzeptiert immer alles, was Git vorschlägt, ohne Aufforderung.quelle
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i" ...
).Was mich am Fixup-Workflow wirklich störte, war, dass ich selbst herausfinden musste, in welches Commit ich die Änderung jedes Mal einfließen lassen wollte. Ich habe einen "git fixup" Befehl erstellt, der dabei hilft.
Dieser Befehl erstellt Fixup-Commits mit der zusätzlichen Magie, dass Git-Deps verwendet werden , um das relevante Commit automatisch zu finden. Daher läuft der Workflow häufig auf Folgendes hinaus :
Dies funktioniert nur, wenn die bereitgestellten Änderungen eindeutig einem bestimmten Commit im Arbeitsbaum (zwischen Master und HEAD) zugeordnet werden können. Ich finde, dass dies sehr oft bei kleinen Änderungen der Fall ist, für die ich dies verwende, z. B. Tippfehler in Kommentaren oder Namen neu eingeführter (oder umbenannter) Methoden. Ist dies nicht der Fall, wird mindestens eine Liste der Kandidaten-Commits angezeigt.
Ich benutze diese viel in meinem täglichen Arbeitsablauf, um schnell kleine Änderungen an zuvor geänderten Zeilen in Commits auf meinem Arbeitszweig zu integrieren. Das Skript ist nicht so schön wie es sein könnte, und es ist in zsh geschrieben, aber es hat die Arbeit für mich schon eine Weile gut genug gemacht, da ich nie das Bedürfnis hatte, es neu zu schreiben:
https://github.com/Valodim/git-fixup
quelle
Mit diesem Alias können Sie eine Korrektur für eine bestimmte Datei erstellen .
Wenn Sie einige Änderungen vorgenommen haben, diese
myfile.txt
aber nicht in ein neues Commit einfügen möchten,git fixup-file myfile.txt
erstellen Sie einfixup!
für das Commit, in demmyfile.txt
zuletzt Änderungen vorgenommen wurden, und dannrebase --autosquash
.quelle
git rebase
das nicht automatisch aufgerufen wurde.commit --fixup
undrebase --autosquash
sind großartig, aber sie tun nicht genug. Wenn ich eine Folge von Commits habeA-B-C
und weitere Änderungen in meinen Arbeitsbaum schreibe, die zu einem oder mehreren dieser vorhandenen Commits gehören, muss ich den Verlauf manuell betrachten, entscheiden, welche Änderungen zu welchen Commits gehören, sie inszenieren und die erstellenfixup!
begeht. Aber git hat bereits Zugriff auf genügend Informationen, um all das für mich tun zu können. Deshalb habe ich ein Perl-Skript geschrieben, das genau das tut.Für jeden Teil im
git diff
Skript wird verwendetgit blame
, um das Commit zu finden, das zuletzt die relevanten Zeilen berührt hat, undgit commit --fixup
um die entsprechendenfixup!
Commits zu schreiben , wobei im Wesentlichen das gleiche getan wird, was ich zuvor manuell getan habe.Wenn Sie es nützlich finden, können Sie es gerne verbessern und wiederholen. Vielleicht erhalten wir eines Tages eine solche Funktion
git
. Ich würde gerne ein Tool sehen, das verstehen kann, wie ein Zusammenführungskonflikt gelöst werden sollte, wenn er durch eine interaktive Rebase eingeführt wurde.quelle
Ich habe eine kleine Shell-Funktion geschrieben, die aufgerufen wird
gcf
, um das Fixup-Commit und die Rebase automatisch durchzuführen:Beispielsweise können Sie das zweite Commit vor dem letzten mit folgendem Patch patchen:
gcf HEAD~~
Hier ist die Funktion . Sie können es in Ihre einfügen
~/.bashrc
Es wird verwendet
--autostash
, um nicht festgeschriebene Änderungen zu speichern und bei Bedarf zu speichern.--autosquash
erfordert eine--interactive
Rebase, aber wir vermeiden die Interaktion durch die Verwendung eines DummysEDITOR
.--no-fork-point
schützt Commits vor dem stillschweigenden Löschen in seltenen Situationen (wenn Sie einen neuen Zweig verlassen haben und jemand bereits frühere Commits neu basiert hat).quelle
Mir ist kein automatisierter Weg bekannt, aber hier ist eine Lösung, die sich möglicherweise leichter von Menschen botieren lässt:
quelle
git fixup
Befehl zu implementieren .--autosquash
Ich würde https://github.com/tummychow/git-absorb empfehlen :
quelle