Blog

Git-Autostash

MAY 25, 2016

Kürzlich leitete ich eine einführende Git-Schulung, als mich einige Nutzer, die derzeit Mercurial verwenden, etwas Interessantes fragten. Frei formuliert:

Jan Krag

Jan has been with us since 2014 as a Continuous Improvement Agent. He is an accredited trainer for Docker and Github and is also a certified life coach. Jan has a truly diverse range of interests. He breeds oriental cats, builds and flies his own kites, collects Rubik’s cubes, and enjoys snowboarding. Wow!

Warum kann Git nicht …?

Warum kann ich nicht pullen, wenn mein Workspace ungespeicherte Änderungen enthält, obwohl Mercurial das standardmäßig kann?

Meine spontane Antwort war, dass Git den Benutzer damit lediglich vor potenziell schädlichen und vor allem irreversiblen Änderungen schützt. Standardmäßig verfolgt Git bei allen Operationen, die Dateien mit ungespeicherten Änderungen im Dateisystem verändern, einen sehr vorsichtigen Ansatz, wenn Git diese Änderungen nicht wieder rückgängig machen kann. Das wird normalerweise als Feature betrachtet.

Der bekannte „Workaround“ beziehungsweise mögliche Workflow besteht darin, Änderungen vor einem Pull zu staschen (mit git stash save) und sie danach wieder zurückzuholen (git stash pop).

Offensichtlich lässt sich das einfach mit einem Git-Alias automatisieren. Allerdings ist das nicht trivial, da git stash nicht sauber fehlschlägt, wenn keine lokalen Änderungen vorhanden sind.

Git autostash

Nach dem Training habe ich mich etwas mit aktuellen Änderungen in Git beschäftigt und dazu gelesen. Dabei stellte sich heraus, dass Git dies inzwischen tatsächlich unterstützt – Grund genug für einen kurzen Blogbeitrag.

Seit Git-Version 2.6 bietet git rebase eine Funktion beziehungsweise Option namens autostash.

Aus der Dokumentation zu git rebase:

--autostash --no-autostash Automatically create a temporary stash before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty work-tree. However, use with care: the final stash application after a successful rebase might result in non-trivial conflicts.

Der manuelle Weg

Als ersten Schritt können wir damit folgenden Prozess verwenden:

git fetch, gefolgt von: git rebase --autostash

Dadurch werden lokale Änderungen automatisch gestasht, Git erledigt „das Richtige“ (also lokale Commits auf die eingehenden Commits rebased) und holt anschließend eure Änderungen wieder zurück.

Leider gibt es keinen entsprechenden Schalter für git pull, auch nicht bei git pull --rebase. Aber keine Sorge.

Git config verwenden

Glücklicherweise und wenig überraschend bietet Git eine entsprechende Konfigurationsoption namens pull.rebase.

Da git pull --rebase intern git rebase verwendet, wird dieser Konfigurationsparameter berücksichtigt.

Das bedeutet, dass ihr Folgendes ausführen könnt:

git config rebase.autoStash true

Danach übernimmt git pull --rebase künftig automatisch das Autosquash-Verhalten.

Alles oder nichts

Wenn ihr euch vollständig auf das Rebase-Verhalten festlegen möchtet, könnt ihr es mit der Konfigurationsoption pull.rebase true kombinieren:

git config --global pull.rebase true git config --global rebase.autoStash true

Dadurch verwendet jedes git pull immer den „rebase“-Prozess, kombiniert mit dem automatischen Stashen und Zurückholen lokaler Änderungen. Ziemlich praktisch …

Mögliche Konflikte

An dieser Stelle sei auf die offensichtliche und unvermeidbare Situation hingewiesen, dass die gestashten Änderungen mit eingehenden, gepullten Änderungen in Konflikt geraten können.

Die resultierende Ausgabe würde etwa so aussehen:

✗ git pull Created autostash: 094ad5c HEAD is now at d39c25c repo1 - readme First, rewinding head to replay your work on top of it... Fast-forwarded master to 6b6e1d4262fd5bc8d2b974f81222003a6c67fea6. Applying autostash resulted in conflicts. Your changes are safe in the stash. You can run "git stash pop" or "git stash drop" at any time.

Im Ergebnis geht git auf Nummer sicher. Es versetzt euren Workspace in einen Konfliktzustand, den ihr wie gewohnt mit eurem bevorzugten Editor oder Merge-Tool auflösen könnt. Gleichzeitig bleibt der Stash sicher erhalten, falls ihr beispielsweise einen Hard Reset durchführen und es erneut versuchen möchtet:

➜ git stash list stash@{0}: autostash

Stattdessen einen Alias verwenden

Wenn ihr euch nicht auf diesen Workflow festlegen und die mehrstufigen Befehle git fetch und git rebase --autostash nicht ausführen möchtet, könnt ihr einen passenden Alias erstellen.

Ein Vorschlag wäre:

git config --global alias.up '!git fetch && git rebase --autostash FETCH_HEAD'

Dann könnt ihr git up gezielt verwenden, wenn ihr mit dem Verfahren Rebase + Autostash aktualisieren möchtet.

  • DevOps
  • CI/CD

Subscribe to our newsletter