Praktische Anwendungen von Git Reset - Soft?

134

Ich arbeite seit etwas mehr als einem Monat mit Git. Ich habe zwar erst gestern zum ersten Mal einen Reset verwendet, aber der Soft-Reset macht für mich immer noch keinen Sinn.

Ich verstehe, dass ich den Soft-Reset verwenden kann, um ein Commit zu bearbeiten, ohne den Index oder das Arbeitsverzeichnis zu ändern, wie ich es mit tun würde git commit --amend.

Sind diese beiden Befehle wirklich gleich ( reset --softvs commit --amend)? Gibt es einen Grund, das eine oder andere praktisch zu verwenden? Und was noch wichtiger ist: Gibt es reset --softaußer der Änderung eines Commits noch andere Verwendungszwecke ?

AJJ
quelle

Antworten:

108

git resetdreht sich alles um das Bewegen HEAD, und im Allgemeinen ist der Zweig ref .
Frage: Was ist mit dem Arbeitsbaum und dem Index?
Bei Verwendung mit --soft, bewegt sich HEAD, aktualisiert meistens die Zweigreferenz und nur dieHEAD .
Dies unterscheidet sich von commit --amend:

  • Es wird kein neues Commit erstellt.
  • Es kann HEAD tatsächlich in ein beliebiges Commit verschieben (da commit --amendes nur darum geht , HEAD nicht zu verschieben, während das aktuelle Commit wiederholt werden kann).

Ich habe gerade dieses Beispiel für das Kombinieren gefunden:

  • eine klassische Verschmelzung
  • eine Teilbaumzusammenführung

Alles in einem (Octopus, da mehr als zwei Zweige zusammengeführt werden) Commit Merge.

Tomas "wereHamster" Carnecky erklärt in seinem Artikel "Subtree Octopus Merge" :

  • Die Strategie zum Zusammenführen von Teilbäumen kann verwendet werden, wenn Sie ein Projekt in ein Unterverzeichnis eines anderen Projekts zusammenführen und anschließend das Teilprojekt auf dem neuesten Stand halten möchten. Es ist eine Alternative zu Git-Submodulen.
  • Mit der Octopus-Merge-Strategie können drei oder mehr Zweige zusammengeführt werden. Die normale Strategie kann nur zwei Zweige zusammenführen. Wenn Sie versuchen, mehr als das zusammenzuführen, greift git automatisch auf die Octopus-Strategie zurück.

Das Problem ist, dass Sie nur eine Strategie auswählen können. Aber ich wollte beide kombinieren, um einen sauberen Verlauf zu erhalten, in dem das gesamte Repository atomar auf eine neue Version aktualisiert wird.

Ich habe ein Superprojekt, nennen wir es projectA, und ein Unterprojekt projectB, das ich in ein Unterverzeichnis von zusammengeführt habe projectA.

(das ist der Teilbaum-Zusammenführungsteil)

Ich halte auch ein paar lokale Verpflichtungen aufrecht.
ProjectAwird regelmäßig aktualisiert, projectBhat alle paar Tage oder Wochen eine neue Version und hängt normalerweise von einer bestimmten Version von ab projectA.

Wenn ich mich entscheide, beide Projekte zu aktualisieren, ziehe ich nicht einfach ab projectAund projectB da dies zwei Commits für ein atomares Update des gesamten Projekts erzeugen würde .
Stattdessen erstelle ich ein einzelnes Merge-Commit, das kombiniert wird projectA, projectBund meine lokalen Commits .
Der schwierige Teil hier ist, dass dies eine Octopus-Zusammenführung (drei Köpfe) ist, aber projectBmit der Teilbaumstrategie zusammengeführt werden muss . Das mache ich also:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Hier verwendete der Autor a reset --hardund stellte dann read-treewieder her, was die ersten beiden Zusammenführungen mit dem Arbeitsbaum und dem Index bewirkt hatten. Hier reset --softkann jedoch
Folgendes hilfreich sein: Wie kann ich die beiden Zusammenführungen wiederholen , die funktioniert haben, dh mein Arbeitsbaum und mein Index sind gut, aber ohne diese beiden Commits aufzeichnen zu müssen?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Jetzt können wir Tomas 'Lösung wieder aufnehmen:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

Also jedes Mal:

  • Sie sind zufrieden mit dem, was Sie am Ende haben (in Bezug auf Arbeitsbaum und Index).
  • Sie sind nicht zufrieden mit all den Verpflichtungen, die Sie dorthin gebracht haben:

git reset --soft ist die Antwort.

VonC
quelle
8
Hinweis für sich selbst: einfaches Beispiel für das Quetschen mit git reset --soft: stackoverflow.com/questions/6869705/…
VonC
3
Dies ist auch nützlich, wenn Sie sich für den falschen Zweig entschieden haben. Alle Änderungen gehen zurück zum Staging-Bereich und gehen mit Ihnen, während Sie den richtigen Zweig auschecken.
Smoebody
44

Anwendungsfall - Kombinieren Sie eine Reihe lokaler Commits

"Ups. Diese drei Commits könnten nur eine sein."

Machen Sie also die letzten 3 (oder was auch immer) Commits rückgängig (ohne den Index oder das Arbeitsverzeichnis zu beeinflussen). Übernehmen Sie dann alle Änderungen als eine.

Z.B

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

Jetzt bleiben alle Ihre Änderungen erhalten und können als eine festgeschrieben werden.

Kurze Antworten auf Ihre Fragen

Sind diese beiden Befehle wirklich gleich ( reset --softvs commit --amend)?

  • Nein.

Gibt es einen Grund, das eine oder andere praktisch zu verwenden?

  • commit --amend Hinzufügen von / rm-Dateien aus dem allerletzten Commit oder Ändern der Nachricht.
  • reset --soft <commit> mehrere sequentielle Commits zu einem neuen zu kombinieren.

Und was noch wichtiger ist: Gibt es reset --softaußer der Änderung eines Commits noch andere Verwendungszwecke ?

  • Siehe andere Antworten :)
Shaun Luttin
quelle
2
Siehe andere Antworten für " reset --soft
Gibt
Als Antwort auf den letzten Kommentar - teilweise vereinbart, aber ich würde sagen, dass die Änderung eines einzelnen Commits zu spezifisch ist. Zum Beispiel möchten Sie vielleicht, sobald eine Funktion geschrieben ist, alles zerquetschen und neue Commits herausarbeiten, die für Prüfer nützlicher sind. Ich würde sagen, dass Soft-Resets (einschließlich Plain git reset) gut sind, wenn Sie (a) den Verlauf neu schreiben möchten, (b) sich nicht um die alten Commits kümmern (damit Sie die Aufregung des interaktiven Rebases vermeiden können) und (c) mehr als ein Commit zur Änderung haben (ansonsten commit --amendist es einfacher).
Johncip
18

Ich verwende es, um mehr als nur das letzte Commit zu ändern .

Nehmen wir an, ich habe einen Fehler in Commit A gemacht und dann Commit B gemacht. Jetzt kann ich nur noch B ändern git reset --soft HEAD^^.

Natürlich ist es für große Commits nicht sehr praktisch… aber du solltest sowieso keine großen Commits machen ;-)

Simon
quelle
3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xfunktioniert auch gut.
Niklas
3
git rebase --interactive HEAD^^Hier können Sie die Commits A und B bearbeiten. Auf diese Weise bleiben die Commit-Nachrichten von A und B erhalten. Bei Bedarf können Sie diese auch noch ändern.
Paul Pladijs
2
Wie kann ich B erneut festschreiben, nachdem ich auf A zurückgesetzt habe?
Northben
1
Eine weitere Möglichkeit , die wahrscheinlich weniger Arbeit: überprüfen Sie Ihre git log, erhalten die Hash B begehen, dann git reset Amachen , und fügen Sie Änderungen git commit --amend, git cherry-pick <B-commit-hash>.
naught101
15

Eine andere mögliche Verwendung ist eine Alternative zum Verstecken (was manche Leute nicht mögen, siehe z . B. https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ ).

Wenn ich zum Beispiel an einem Zweig arbeite und dringend etwas am Master reparieren muss, kann ich einfach Folgendes tun:

git commit -am "In progress."

dann checkout master und mach das fix. Wenn ich fertig bin, kehre ich zu meiner Filiale zurück und mache es

git reset --soft HEAD~1

weiterarbeiten, wo ich aufgehört habe.

Deltacrux
quelle
3
Update (jetzt, wo ich Git besser verstehe): --softist hier eigentlich unnötig, es sei denn, Sie möchten wirklich, dass die Änderungen sofort bereitgestellt werden. Ich benutze jetzt nur, git reset HEAD~wenn ich das mache. Wenn ich einige Änderungen haben statt wenn ich brauche Zweige zu wechseln, und wollen es auch bleiben, dann mache ich git commit -m "staged changes", dann git commit -am "unstaged changes", später git reset HEAD~gefolgt von git reset --soft HEAD~vollständig den Arbeitszustand wiederherzustellen. Obwohl, um ehrlich zu sein, ich diese beiden Dinge jetzt, wo ich weiß, viel weniger git-worktree
mache
7

Sie können git reset --softdie Version ändern, die Sie als übergeordnetes Element für die Änderungen in Ihrem Index und Arbeitsbaum haben möchten. Die Fälle, in denen dies nützlich ist, sind selten. Manchmal können Sie entscheiden, dass die Änderungen, die Sie in Ihrem Arbeitsbaum haben, zu einem anderen Zweig gehören sollen. Oder Sie können dies als einfache Methode verwenden, um mehrere Commits zu einem zusammenzufassen (ähnlich wie Squash / Fold).

In dieser Antwort von VonC finden Sie ein praktisches Beispiel: Squash die ersten beiden Commits in Git?

Johannes Rudolph
quelle
Das ist eine gute Frage, die ich vorher nicht gefunden hatte. Ich bin mir jedoch nicht sicher, wie ich mit reset soft Änderungen an einem anderen Zweig vornehmen kann. Also habe ich Checkout Branch, dann benutze ich git reset --soft anotherBranchund verpflichte mich dort? Aber Sie ändern den Ckeckout-Zweig nicht wirklich. Werden Sie sich also für den Zweig oder eine andere Branche entscheiden ?
AJJ
Wenn Sie dies tun, ist es wichtig, dass Sie kein Git Checkout verwenden, da dies Ihren Baum verändert. Stellen Sie sich git reset --soft als eine Möglichkeit vor, nur die Revision zu manipulieren, auf die HEAD verweist.
Johannes Rudolph
7

Eine mögliche Verwendung wäre, wenn Sie Ihre Arbeit auf einem anderen Computer fortsetzen möchten. Es würde so funktionieren:

  1. Kasse einer neuen Filiale mit einem versteckten Namen,

    git checkout -b <branchname>_stash
    
  2. Schieben Sie Ihren Stash-Zweig nach oben,

    git push -u origin <branchname>_stash
    
  3. Wechseln Sie zu Ihrem anderen Computer.

  4. Ziehen Sie sowohl Ihren Vorrat als auch die vorhandenen Zweige herunter.

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. Sie sollten sich jetzt in Ihrer bestehenden Niederlassung befinden. Führen Sie die Änderungen aus dem Stash-Zweig zusammen.

    git merge <branchname>_stash
    
  6. Setzen Sie Ihren vorhandenen Zweig vor dem Zusammenführen auf 1 zurück.

    git reset --soft HEAD^
    
  7. Entfernen Sie Ihren Versteckzweig,

    git branch -d <branchname>_stash
    
  8. Entfernen Sie auch Ihren Stash-Zweig vom Ursprung.

    git push origin :<branchname>_stash
    
  9. Arbeiten Sie weiter mit Ihren Änderungen, als hätten Sie sie normal aufbewahrt.

Ich denke, in Zukunft werden GitHub und Co. sollte diese "Remote Stash" -Funktionalität in weniger Schritten anbieten.

llaughlin
quelle
2
Ich möchte darauf hinweisen, dass der erste Stash und Pop auf Ihrem ersten Computer völlig unnötig sind. Sie können einfach einen neuen Zweig direkt aus einer schmutzigen Arbeitskopie erstellen, festschreiben und die Änderungen dann auf die Fernbedienung übertragen.
7

Eine praktische Anwendung ist, wenn Sie sich bereits für Ihr lokales Repo entschieden haben (z. B. git commit -m), können Sie dieses letzte Commit durch Zurücksetzen von git --soft HEAD ~ 1 rückgängig machen

Auch für Ihr Wissen, wenn Sie Ihre Änderungen bereits inszeniert haben (dh mit git add.), Können Sie die Inszenierung umkehren, indem Sie git reset --mixed HEAD ausführen, oder ich habe gewöhnlich auch nur verwendetgit reset

Zuletzt löscht git reset --hard alles aus, einschließlich Ihrer lokalen Änderungen. Der ~ after-Kopf gibt an, zu wie vielen Commits Sie von oben gehen müssen.

j2emanue
quelle
1
git reset --soft HEAD ~1gebe ich fatal: Cannot do soft reset with paths.mir denke , dass wir den Raum nach HEAD entfernen müssen , so dass es sein würdegit reset --soft HEAD~1
Andrew Lohr
6

Ein guter Grund, ' git reset --soft <sha1>' zu verwenden, ist, sich HEADin einem nackten Repo zu bewegen .

Wenn Sie versuchen, die Option --mixedoder zu verwenden --hard, wird eine Fehlermeldung angezeigt, da Sie versuchen, einen nicht vorhandenen Baum und / oder Index zu ändern und zu bearbeiten.

Hinweis: Sie müssen dies direkt vom Bare Repo aus tun.

Hinweis: Sie müssen sicherstellen, dass der Zweig, den Sie im Bare Repo zurücksetzen möchten, der aktive Zweig ist. Wenn nicht, befolgen Sie die Antwort von VonC zum Aktualisieren des aktiven Zweigs in einem Bare-Repo, wenn Sie direkten Zugriff auf das Repo haben.

Hazok
quelle
1
Wie in Ihrer vorherigen Antwort ( stackoverflow.com/questions/4624881/… ) erwähnt, gilt dies, wenn Sie direkten Zugriff auf das Bare Repo haben (das Sie hier erwähnen). +1 obwohl.
VonC
@VonC Ja, absolut korrekt und danke für das Hinzufügen der Notiz! Ich vergesse immer wieder, das hinzuzufügen, da ich davon ausgehe, dass das Zurücksetzen direkt vom Repo aus erfolgt. Außerdem gehe ich davon aus, dass der Zweig, den die Person im Bare Repo zurücksetzen möchte, ihr aktiver Zweig ist. Wenn der Zweig nicht der aktive Zweig ist, muss gemäß Ihrer Antwort ( stackoverflow.com/questions/3301956/… ) aktualisiert werden, wie der aktive Zweig für reine Repos aktualisiert werden soll. Ich werde die Antwort auch mit den aktiven Zweiginformationen aktualisieren. Danke noch einmal!!!
Hazok
2

SourceTree ist eine Git-GUI mit einer recht praktischen Oberfläche, auf der Sie genau die gewünschten Bits bereitstellen können. Es gibt nichts Ähnliches für die Änderung einer ordnungsgemäßen Überarbeitung.

Ist git reset --soft HEAD~1also viel nützlicher als commit --amendin diesem Szenario. Ich kann das Commit rückgängig machen, alle Änderungen wieder in den Staging-Bereich übertragen und die bereitgestellten Bits mit SourceTree weiter optimieren.

Wirklich, es scheint mir, dass dies commit --amendder redundantere Befehl der beiden ist, aber git ist git und scheut sich nicht vor ähnlichen Befehlen zurück, die etwas andere Dinge tun.

Roman Starkov
quelle
1

Obwohl mir die Antworten in diesem Thread git reset --softsehr gut gefallen, verwende ich sie für ein etwas anderes, aber dennoch sehr praktisches Szenario.

Ich verwende eine IDE für die Entwicklung, die ein gutes Diff-Tool zum Anzeigen von Änderungen (inszeniert und nicht inszeniert) nach meinem letzten Commit enthält. Jetzt beinhalten die meisten meiner Aufgaben mehrere Commits. Nehmen wir zum Beispiel an, ich mache 5 Commits, um eine bestimmte Aufgabe zu erledigen. Ich verwende das Diff-Tool in der IDE während jedes inkrementellen Commits von 1 bis 5, um meine Änderungen gegenüber dem letzten Commit zu überprüfen. Ich finde es sehr hilfreich, meine Änderungen vor dem Festschreiben zu überprüfen.

Wenn ich jedoch am Ende meiner Aufgabe alle meine Änderungen zusammen (vor dem ersten Festschreiben) anzeigen möchte, um vor dem Abrufen einer Pull-Anforderung eine Selbstcodeüberprüfung durchzuführen, werden nur die Änderungen aus meinem vorherigen Festschreiben (nach dem Festschreiben) angezeigt 4) und nicht von allen Commits meiner aktuellen Aufgabe geändert.

Also git reset --soft HEAD~4gehe ich 4 Commits zurück. Dadurch kann ich alle Änderungen zusammen sehen. Wenn ich von meinen Änderungen überzeugt bin, kann ich git reset HEAD@{1}sie sicher auf die Fernbedienung übertragen.

Swanky Coder
quelle
1
... dann git add --patchund git commitwiederholt, um die Commit-Serie zu erstellen, die Sie erstellt hätten, wenn Sie gewusst hätten, was Sie die ganze Zeit getan haben. Ihre ersten Verpflichtungen sind wie die Notizen auf Ihrem Schreibtisch oder der erste Entwurf eines Memos. Sie dienen dazu, Ihr Denken zu organisieren und nicht zur Veröffentlichung.
Bis zum
Hey, ich habe nicht ganz verstanden, was du vorschlägst.
Swanky Coder
1
Anstatt git reset @{1}Ihre erste Entwurfsreihe wiederherzustellen, können Sie stattdessen eine Reihe für Veröffentlichungen aus git add -pund erstellen git commit.
Bis zum
Ja richtig. Eine andere Möglichkeit ist (die, der ich normalerweise folge), auf Upstream / Master neu zu gründen und die Commits in eine einzige zu zerlegen.
Swanky Coder
1

Ein anderer Anwendungsfall ist, wenn Sie den anderen Zweig in einer Pull-Anfrage durch Ihren ersetzen möchten. Nehmen wir beispielsweise an, Sie haben eine Software mit den Funktionen A, B, C in der Entwicklung.

Sie entwickeln mit der nächsten Version und Sie:

  • Funktion B entfernt

  • Feature D hinzugefügt

Entwickeln Sie dabei gerade hinzugefügte Hotfixes für Feature B.

Sie können die Entwicklung in die nächste zusammenführen, aber das kann manchmal chaotisch sein, aber Sie können auch git reset --soft origin/developein Commit mit Ihren Änderungen verwenden und erstellen, und der Zweig kann ohne Konflikte zusammengeführt werden und Ihre Änderungen beibehalten.

Es stellt sich heraus, dass dies git reset --softein praktischer Befehl ist. Ich persönlich benutze es häufig, um Commits zu quetschen, die keine "abgeschlossene Arbeit" wie "WIP" haben. Wenn ich also die Pull-Anfrage öffne, sind alle meine Commits verständlich.

cnexans
quelle