Powiedzmy wprost: git, czyli system kontroli wersji, będący obecnie standardem w branży IT, do intuicyjnych narzędzi nie należy. Fakt, że kiedy zrozumiemy już podstawowe koncepcje i prawa rządzące światem gita, praca z nim staje się szybka i niezwykle efektywna. Niestety krzywą uczenia git ma dość stromą i nawet doświadczonemu użytkownikowi zdarza się czasem jedną źle sformułowaną komendą tak namieszać w naszym repozytorium, że wszechobecne spoty gałęzi gita stają się splotami Czerwia Pustyni, a my czujemy się jak Atrydzi na Diunie - bez nadziei na ratunek ;)
W tej serii artykułów pokażę Wam kilka komend mocy gita, które pozwolą Wam poczuć się jak mistrz Jedi i przywrócić porządek w Galaktyce (repozytorium). Dowiecie się jak bez ryzyka podróżować po splotach branchy gita, jak wytropić zaginione zmiany i jak szybko odnaleźć commit, który wprowadził zamieszanie w repozytorium. Do dzieła!
Na start
Pierwszą komendą, którą chcę opisać jest git stash, czyli schowek. Bardzo często używam go podczas pracy, ponieważ pozwala na szybkie zapisywanie zmian w podręcznym „schowku”, który nie zmienia struktury repozytorium i zaaplikowanie ich na dowolnym commicie.
Czym jest stash?
Czym zatem jest git stash?
Jest to mechanizm, który umożliwia zapisanie zmian w lokalnym repozytorium i jednoczesne przywrócenie stanu working directory do stanu określonego przez wskaźnik HEAD. Zapisane zmiany trafiają do „stash” czyli „schowka”, skąd będą mogły być później odzyskane. Schowek jest mechanizmem niezależnym od commitów i loga, świetnie nadaje się zatem do przechowywania zmian stanowiących Work-in-progress, gdyż nie jest uwzględniany w historii commitów i nie zostaje wypchnięty do zdalnego repozytorium.
Aby skorzystać ze schowka, wystarczy utworzyć nowy plik, lub dokonać zmian w istniejącym i wywołać polecenie git stash:
Komenda git stash zwróci nam następujące informacje: 1. informacje o zapisaniu working directory, 2. informacje o zapisaniu stanu indexu, 3. nazwę brancha i hash commita, na które wskazuje HEAD w momencie utworzenia wpisu w schowku, czyli potocznie tzw. ,,stasha”. Tutaj uwaga: ,,stash” jest zarówno poleceniem, mechanizmem wewnątrz gita, który służy do zapisywania podręcznych zmian (bez zaburzania historii commitów), jak również potocznym określeniem pojedynczego zapisu, znajdującego się w schowku. Ale do tego, co wymienione powyżej informacje oznaczają jeszcze wrócimy.
Domyślnie git stash zapisuje zmiany, które zostały wcześniej dodane do indexu. Możemy to zmienić dodając flagę -u lub —include-untracked. Trzeba jeszcze dodać, że schowek działa na zasadzie LIFO (last-in-first-out), co oznacza, że ostatni dodany element będzie pierwszym, który otrzymamy z powrotem. Domyślnym formatem wiadomości w poleceniu git stash jest „WIP on {branch-name}: {commit-sha-1-hash} {commit-message}”, ale możemy dodać własną wiadomość za pomocą flagi -m lub —messsage. Dobrze jest dodawać takie opisowe wiadomości, ponieważ przy dużej ilości wpisów w ,,stashu”, domyślne wiadomości nie są zbyt pomocne podczas identyfikacji poszczególnych ,,stashy”.
Domyślnie komenda git stash dodaje do schowka tylko zmiany ze śledzonych plików. Jeśli skorzystamy z flagi -u ,to w ,,stashu” znajdą się również zmiany z nieśledzonych plików. Jeśli chcemy dodać do slasha tylko zmodyfikowane pliki, nieznajdujące się w indeksie, to skorzystamy z flagi —keep-index lub -k.
Wyświetlanie listy
Aby sprawdzić, jakie wpisy znajdują się w „stashu”, trzeba użyć komendy git stash list. Dokładniejszą informację o zmianach zawartych w danym wpisie uzyskamy za pomocą komedy git stash show stash{numer-wpisu}.
Jeśli chcemy zobaczyć jakie zmiany wprowadza dany wpis, skorzystajmy z flagi -p lub —pretty.
Aktywacja zmian
Teraz, kiedy wiemy już jak umieścić nasze zmiany w schowku i zobaczyć, co się w nim znajduje, czas wydobyć nasze zmiany i zaaplikować w naszym working directory. Możemy to zrobić na dwa sposoby.
Po pierwsze możemy skorzystać z komendy git stash apply (podając opcjonalnie numer wpisu ze schowka stash@{numer-stasha}). Wtedy zmiany z naszego schowka zostaną dodane do working directory i jednocześnie będą dalej przechowywane w schowku.
Po drugie możemy skorzystać z git stash pop. Wtedy zmiany zostaną dodane do working directory i jednocześnie ucięte ze schowka.
Czyszczenie stasha
Kiedy stash nie jest już potrzebny, możemy wyczyścić jego zawartość. Służą do tego polecenia clear i drop. Za pomocą drop możemy usunąć wybrany wpis, podając jego numer. Jeśli nie podamy żadnego numeru, usuniemy ostatni wpis.
Polecenie clear usuwa wszystkie zmiany zapisane w stashu.
Utworzenie nowego brancha ze stasha
Może się zdarzyć, że będziemy chcieli utworzyć nowy branch dla zmian, które mamy zapisane w stashu. Służy do tego polecenie git stash branch <nazwa-nowego-brancha stash_id>. Git utworzy wtedy nowego brancha na commicie, z którego utworzyliśmy stash, usunie zmiany ze stasha i doda je do working directory.
Tworzenie stasha bez zmian w reflogu
Git przechowuje ostatni utworzony stash w folderze .git/refs/stash, pozostałe „stashe” znajdują się w reflogu dla referencji ‚stash’. Czasem może się zdarzyć, że będziemy chcieli utworzyć stash bez zmieniania reflogu. Wtedy możemy skorzystać z polecenia git stash create. Utworzy ono stash i zwróci jego sha-1 hash, bez dodawania go do reflogu stasha.
Później będziemy mogli dodać ten obiekt do reflogu stasha za pomocą polecenia git stash store <sha-1-hash>.
W jaki sposób git zachowuje zmiany w stashu?
Kiedy wywołujemy polecenie git stash, git tworzy obiekt typu commit w folderze .git/refs/stash.
Ten commit przechowuje stan working directory.
Kiedy podejrzymy go poleceniem git cat-file -p, zobaczymy, ze ten commit ma dwóch rodziców! Pierwszym jest commit, na którym znajdował się wskaźnik HEAD w momencie wywołania polecania stash. Drugi przechowuje stan indexu w tym momencie.
Ostatni utworzony wpis w stashu jest przechowywany w folderze refs/stash. Starsze schowki znajdują się w reflogu.
Kiedy zatem używać stasha?
Kiedy pracujemy nad zadaniem i nagle musimy wykonać hot-fixa o najwyższym priorytecie. Nasze obecne zmiany są jeszcze niegotowe, więc nie będziemy ich commitować, dlatego zachowamy je w stashu. Możemy teraz przenieść się na dowolnego brancha, dokonać niezbędnych poprawek, po czym wrócić do przerwanej pracy.
Może zdarzyć się, że w ferworze walki z jakimś wyjątkowo złośliwym bugiem nie zorientujemy się, że pracujemy na niewłaściwym branchu. Manualne kopiowanie zmian nie jest na szczęście konieczne, wystarczy zachować zmiany w stashu i zaaplikować na właściwym branchu.
Kiedy jesteśmy w trakcie pracy nad zmianą i nagle dowiadujemy się, że w zdalnym repozytorium zaszły zmiany, które musimy włączyć do naszej pracy. Git pull odmawia nadpisania naszych zmian - wystarczy schować zmiany w stashu, wykonać git pull, a następnie odzyskać zmiany ze schowka
Mam nadzieję, że ta użyteczna komenda pomoże Wam pokonać lęk przed utraceniem zmian, kiedy przełączacie się między branchami. Być może poprawi Waszą swobodę w pracy z gitem i przybliży Was do gitowego „mistrzostwa” :) Ja osobiście korzystam ze stasha bardzo często, zwłaszcza kiedy otrzymuję dużo „wrzutek”, a jednocześnie muszę pracować nad bieżącymi zadaniami.