Koniec z git-checkout

Git to obecnie w zasadzie synonim systemu kontroli wersji – mało kto dzieli się otwartym kodem w inny sposób, ale też przy pracy nad zamkniętym komercyjnym kodem coraz rzadziej widzi się inne rozwiązania. Ważąc wszystkie za i przeciw, jest to prawdopodobnie bardzo pozytywna zmiana, jeśli przypomnieć sobie z jakimi potworkami zmuszeni byli nie tak dawno temu pracować programiści. Tyle że biorąc Gita za coś oczywistego możemy zacząć zapominać o jego wadach – a nie jest to idealny system kontroli wersji. Narzędzie stworzone przez Linusa Torvaldsa było chyba od początku mocno krytykowane za user experience: że ma dziwną logikę, podporządkowaną bardziej sposobowi działania samego oprogramowania, a nie temu, jak pracę z wersjami widzi człowiek. Najmocniej przejawia się to w komendach, które robią kilka bardzo odległych koncepcyjnie rzeczy w zależności od podanych argumentów. Na przykład checkout: czasem odrzuca niezacommitowane zmiany w plikach, a czasem zmienia gałęzie.

Znalazłem ostatnio instrukcje, jak pozbyć się git checkout, korzystając z nowych poleceń switch i restore. W dalszej części opowiem, czemu warto tak zrobić i jak wygląda praca na nowy sposób.

Co jest złego z git-checkout?

Wracając do wspomnianej wcześniej krytyki, to do mnie osobiście długo nie trafiała. Gita nauczyłem się dawno temu, w tamtym czasie innych systemów kontroli wersji używałem na tyle niedługo, że nie miałem mocno zakorzenionych „obcych” nawyków, których musiałem się oduczać, żeby dobrze wpasować się w Gita. Tak cieszyłem się widząc, że rzeczy będące koszmarami w „starych” systemach daje się w Gicie ogarnąć wydajnie i bez łamania sobie głowy, że problemy z „ergonomią” linii komend nie przychodziły mi nawet do głowy. Gdy czytałem w sieci krytykę, że polecenia są niejasne, stwierdzałem, że co z tego – narzędzie jest jakie jest, lepszego nie ma, więc po co drążyć temat, a jak ktoś ma problemy z linią komend, to widocznie za rzadko używa Gita, więc niech nie krytykuje, tylko ćwiczy.

Aż ostatnio, mając już chyba z 10 lat doświadczenia z Gitem, nie skasowałem sobie pracy. Chciałem w repozytorium przełączyć gałąź na „develop”, wpisałem coś na szybko w konsoli, pewnie git checkout d<tab><enter>. Tyle że w tym repozytorium nie było gałęzi „develop”, była tylko „master”. Uruchomione przeze mnie tabulacją uzupełnianie nie mogło po prostu dopisać „evelop”, bo nic takiego nie istniało, ale to nie znaczy, że mój shell się poddał. Zsh słusznie słynie z inteligentnego uzupełniania, tutaj powłoka „wiedziała”, że kandydatami do uzupełnienia mogą być gałęzie (do przełączenia) albo zmodyfikowane pliki (do „wyczyszczenia” ze zmian). Pech chciał, że poczyściłem repozytorium z nieużywanych gałęzi, więc żadnego dopasowania na gałęziach nie było, za to literkę „d” w nazwie miał plik, w którym w pocie czoła dłubałem dłuższy czas, ale nie wiedziałem, czy zmiany w nim zachować, czy je porzucić. Ostatecznie więc zamiast przełączyć gałąź, wykasowałem rezultaty mojej pracy.

Mogłem powiedzieć: „shit happens”, to był jednorazowy wypadek, świat się mimo wszystko nie zawalił, trzeba o tym zapomnieć i działać jakby nic się nie stało. Albo postanowić na przyszłość, że dwa razy zastanowię się przed każdym użyciem uzupełniania w shellu. Albo przestać używać Zsh, bo jest zbyt dobre w uzupełnianiu. Tyle że postanowiłem, że nie mam zamiaru spowalniać swojej pracy i to z komendą Gita jest coś nie tak, skoro czasem jest zupełnie bezpieczna (przełączanie gałęzi nie może kasować zmian), a czasem bezpowrotnie kasuje dane. W dobrze zaprojektowanym narzędziu byłby jeden sposób działania do przełączania gałęzi i używając go nie musiałbym się trzymać na baczności, a drugi, zupełnie oddzielny, do kasowania danych i wtedy musiałbym bardziej uważać, co robię.

Stwierdziłem, że tylko krowa nie zmienia poglądów i trzeba coś zrobić z git-checkout.

Cały problem z git-checkout: potrafi zarówno przełączyć na branch „improvement” jak i skasować zmiany w pliku „index.sh” – pośpiech w uzupełnianiu tabulatorem może być tragiczny w skutkach

Nie trzeba używać git-checkout

Okazało się, że twórcy Gita nie są głusi na krytykę i w wersji 2.23 wprowadzono 2 nowe komendy mogące zastąpić checkout:

  • git switch do przełączania się między gałęziami
  • git restore do przywracania plików do stanu sprzed zmian

Niby obie mają cały czas status „eksperymentalnych”, ale od ich debiutu minął rok, są nawet obsługiwane w podpowiadaniu Zsh.

Tak nie skasuję sobie przypadkiem zmian: git-switch służy tylko do zmiany gałęzi

Żeby przełączyć gałąź na „improvement”, wpisuję git switch improvement (odpowiednik git checkout improvement).

Żeby stworzyć gałąź „bugfix”, wpisuję git switch -c bugfix (odpowiednik git checkout -b bugfix).

Trzeba trochę zmienić przyzwyczajenia, gdy przeskakujemy na coś, co nie jest lokalną gałęzią. Mogę zrobić git checkout HEAD^^ albo git checkout origin/bugfix, ale git-switch wymaga dodania opcji -d (detached): git switch -d HEAD^^ i git switch -d origin/bugfix.

Więcej o użyciu obu nowych komand można poczytać w artykule na blogu Bluecast.

Od ponad miesiąca nie używam git-checkout i jestem zadowolony. Jedyne, co mnie gryzie, to gorsze podpowiadanie w Zsh przy przełączaniu gałęzi. Na pierwszym obrazku widać, że dla checkout przy nazwie gałęzi pokazywany jest opis ostatniego commitu – bardzo to pomaga w orientowaniu się. Dla switch wyświetlana jest tylko nazwa gałęzi.

Jak zablokować git-checkout

10 lat to sporo czasu na wykształcenie nawyków i po takim czasie nie trzeba udziału świadomości, żeby palce wpisały na klawiaturze „git checkout”. Był to dla mnie spory problem, gdy starałem się przestawić. Zdecydowałem się na kurację wstrząsową – niech w konsoli wpisanie „git checkout” przestanie działać.

StackOverflow podaje gotowe rozwiązanie. U mnie tamtejszy kod wklejony do ~/.zshrc działa, powinno być tak samo z Bashem, ale nie sprawdzałem.

Pomoc w oduczaniu się starych nawyków