Blog

Git autostash

MAY 25, 2016

När jag nyligen höll en introduktionskurs i Git fick jag en intressant fråga från några användare som för närvarande använde Mercurial. Lite fritt återgivet:

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!

Varför kan inte git …?

Varför kan jag inte pulla när jag har en dirty workspace, när Mercurial kan göra detta direkt?

Mitt direkta svar var att detta helt enkelt är Git:s sätt att skydda användaren från potentiellt skadliga och, ännu viktigare, oåterkalleliga ändringar. Som standard har Git ett mycket försiktigt förhållningssätt till åtgärder som ändrar dirty-filer i ditt filsystem, när Git inte själv kan hjälpa dig att ångra ändringarna. Detta ses normalt som en funktion.

Den kända ”lösningen”, eller ett möjligt arbetsflöde, är att stasha ändringar innan du gör en pull (med git stash save) och sedan återställa dem (med git stash pop) när du är klar.

Det verkar självklart att detta borde vara enkelt att automatisera med ett git-alias, men det visar sig inte vara helt trivialt eftersom git stash inte hanterar avsaknaden av lokala ändringar på ett smidigt sätt.

Git autostash

Efter utbildningen funderade jag lite och läste på om de senaste ändringarna i Git. Det visar sig att Git faktiskt har stöd för detta nu, och jag tyckte att det var värt ett kort blogginlägg.

Sedan Git version 2.6 har git rebase en funktion/ett alternativ som heter autostash.

Från dokumentationen för 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.

Det manuella sättet

Som ett första steg kan vi använda följande process:

git fetch följt av: git rebase --autostash

Detta stashar automatiskt alla lokala ändringar, ”gör rätt sak” (det vill säga rebasa lokala commits ovanpå de inkommande) och återställer sedan dina ändringar igen.

Tyvärr finns det ingen motsvarande flagga i git pull, inte ens när du använder git pull --rebase, men misströsta inte.

Använd git config

Som tur är, och föga förvånande, har git en motsvarande konfigurationsfunktion som heter pull.rebase.

Eftersom git pull --rebase använder git rebase bakom kulisserna respekteras denna konfigurationsparameter.

Det innebär att du kan köra:

git config rebase.autoStash true

Därefter kommer git pull --rebase att använda autostash framöver.

Gå hela vägen

Om du verkligen vill använda rebase-beteendet hela tiden kan du kombinera det med konfigurationsalternativet pull.rebase true:

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

Resultatet är att varje git pull alltid använder ”rebase”-processen tillsammans med automatisk stashning och återställning av lokala ändringar. Riktigt smidigt …

Möjliga konflikter

Här är det värt att nämna den uppenbara och oundvikliga situationen där de stashade ändringarna kan hamna i konflikt med inkommande ändringar som pullas.

Resultatet skulle se ut ungefär så här:

✗ 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.

Resultatet blir att git tar det säkra före det osäkra. Det försätter din arbetsyta i ett konfliktläge som du kan lösa som vanligt med din favoriteditor eller ditt mergeverktyg. Samtidigt sparas stashen säkert ifall du till exempel vill göra en hard reset och försöka igen:

➜ git stash list stash@{0}: autostash

Använd ett alias i stället

Om du inte vill binda dig till det här arbetsflödet och inte vill köra git fetch och git rebase --autostash i flera steg kan du skapa ett lämpligt alias.

Ett förslag är:

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

Därefter kan du helt enkelt använda git up selektivt när du vill uppdatera med rebase + autostash-proceduren.

  • DevOps
  • CI/CD

Subscribe to our newsletter