Git dla początkujących: branch, commit i PR na jednym zadaniu
Git ma grubo ponad sto poleceń, a w zwykłym tygodniu pracy używa się mniej więcej sześciu. Które to sześć, nie dowiesz się z kursu gita. Dowiesz się wtedy, gdy masz zmianę do zrobienia i potrzebujesz bezpiecznego miejsca, żeby ją zrobić. Dlatego w tym tekście jest jeden mały błąd, jedna poprawka i wszystkie polecenia, których ta poprawka wymaga — razem z tym, co odpisze Ci terminal.
Git dla początkujących: ucz się na zadaniu, nie na kursie
Git dla początkujących sprowadza się do jednej pętli: zakładasz branch, zmieniasz kod, robisz commit, wypychasz branch, otwierasz pull request. Kurs uczy tych pięciu kroków jak słówek. Zadanie uczy ich jako odpowiedzi na pytania, które i tak sobie zadajesz: „gdzie to wrzucić, żeby nie zepsuć tego, co działa?" — to branch; „jak zapisać ten stan, żeby dało się do niego wrócić?" — to commit. Git zapisuje zmiany, więc bez zmiany, na której Ci zależy, nie ma na czym ćwiczyć.
Zadanie
Załóżmy, że przeszedłeś plan na pierwszy tydzień i wypchnąłeś wynik na GitHuba: funkcję total_of, która sumuje listę cen, plus dwa testy. I wtedy ktoś podaje jej listę, w której siedzi "free":
total = total + price
~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for +: 'float' and 'str'
Decyzja: wpisy, które nie są liczbami, pomijamy. Trzy linijki Pythona. Python jest dziś tą łatwą częścią.
Najpierw patrz, potem dotykaj
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
git status mówi, na którym branchu jesteś i co się zmieniło od ostatniego commita. Niczego nie modyfikuje, więc odpalaj go przy każdej wątpliwości. Doświadczeni inżynierzy wpisują go bez przerwy i wcale nie z nerwów.
Załóż branch
$ git switch -c fix/skip-non-numeric
Switched to a new branch 'fix/skip-non-numeric'
git switch -c tworzy nowy branch (po polsku: gałąź) i od razu Cię na niego przenosi. Branch to przesuwalna etykieta przyklejona do commita, nic cięższego: od tej chwili nowe commity lądują pod tą etykietą, a main zostaje dokładnie taki, jaki był. Jeśli poprawka okaże się złym pomysłem, kasujesz etykietę i sprawy nie było.
W starszych tutorialach zobaczysz w tym miejscu git checkout -b. Robi to samo; switch pojawił się w Gicie 2.23, bo na checkout zrzucono za dużo niezwiązanych ze sobą obowiązków.
Teraz zrób poprawkę, dopisz trzeci test, który przybija nowe zachowanie, i zapytaj gita, co właściwie zrobiłeś:
$ git diff prices.py
diff --git a/prices.py b/prices.py
index b466e71..e362d7b 100644
--- a/prices.py
+++ b/prices.py
@@ -1,5 +1,7 @@
def total_of(prices):
total = 0
for price in prices:
+ if not isinstance(price, (int, float)):
+ continue
total = total + price
return total
git diff pokazuje linie, którymi Twoje pliki różnią się od ostatniego commita — + to dodane, - to usunięte. Czytaj go przed każdym commitem. Najtańsze code review, jakie kiedykolwiek dostaniesz, to Twoje własne, pół minuty przed tym, zanim zmianę zobaczy ktokolwiek inny.
$ pytest -q
... [100%]
3 passed in 0.01s
Najpierw stage, potem commit
$ git add prices.py test_prices.py
$ git commit -m "Skip non-numeric entries in total_of"
[fix/skip-non-numeric 1ead82b] Skip non-numeric entries in total_of
2 files changed, 6 insertions(+)
git add wkłada bieżący stan tych dwóch plików do staging area — poczekalni, czyli listy tego, co wejdzie do następnego commita. git commit zapisuje zawartość poczekalni na stałe, z Twoim komunikatem i identyfikatorem (tutaj 1ead82b), do którego możesz potem wrócić.
Ten dwutakt na początku myli każdego, ale ma sens: w katalogu roboczym możesz mieć trzy niezwiązane ze sobą zmiany, a commit powinien zawierać jedną. Poczekalnia służy do wybierania. Z tego samego powodu lepiej wymienić pliki z nazwy, niż wpisać git add . — kropka bierze wszystko, razem z debugowym printem, o którym zapomniałeś.
Komunikat pisze się w trybie rozkazującym i mówi, co zmiana robi. „Skip non-numeric entries" będzie zrozumiałe za osiem miesięcy. „fix" i „poprawki" nie będą, a czytać je będziesz Ty.
Push, a potem pull request
$ git push -u origin fix/skip-non-numeric
remote:
remote: Create a pull request for 'fix/skip-non-numeric' on GitHub by visiting:
remote: https://github.com/you/prices/pull/new/fix/skip-non-numeric
remote:
* [new branch] fix/skip-non-numeric -> fix/skip-non-numeric
branch 'fix/skip-non-numeric' set up to track 'origin/fix/skip-non-numeric'.
git push wysyła commity z Twojego brancha do zdalnego repozytorium o nazwie origin. Flaga -u zapamiętuje powiązanie lokalnego brancha ze zdalnym, więc następnym razem wystarczy samo git push.
Zwróć uwagę, kto wypisał link: linie zaczynające się od remote: pochodzą od GitHuba, nie od gita. Pull request nie jest poleceniem gita. Git w ogóle nie wie, co to PR — dość osobliwa rzecz jak na najsłynniejsze słowo z jego okolic. PR to funkcja platformy hostującej (GitLab nazywa to samo merge requestem): strona z komunikatem „proszę o włączenie tego brancha do main", z dołączonym diffem i miejscem na spór. Otwórz link, napisz w dwóch zdaniach, co się zmieniło i dlaczego, wyślij.
Na tej stronie odbywa się code review. Gdy PR zostanie zmergowany, wracasz do domu:
$ git switch main
$ git pull
$ git branch -d fix/skip-non-numeric
Wracasz na main, pobierasz zmergowany wynik, kasujesz etykietę, która już nie jest potrzebna. Cała pętla. Następne zadanie to ta sama pętla z inną nazwą brancha.
Czego zamiast tego uczyłby kurs
Rebase, cherry-pick, reflog i detached HEAD — w pierwszym tygodniu, komuś, kto nie ma jeszcze czego rebase'ować. Wszystkie cztery są prawdziwe i kiedyś się przydadzą. Przydadzą się w dniu, w którym zażąda ich zadanie, i wtedy wyjaśnienie zostanie w głowie, bo będzie przypięte do problemu, który naprawdę masz.
To, co faktycznie pójdzie nie tak w pierwszym miesiącu, jest mniejszego kalibru. Zaczniesz przez pomyłkę edytować na main — wpisz git switch -c mimo to, bo niezacommitowane zmiany przechodzą z Tobą na nowy branch. Trafisz na pierwszy konflikt przy merge'u i będzie wyglądał gorzej, niż jest. Do żadnej z tych rzeczy nie trzeba sylabusa. Do obu trzeba repozytorium, na którym zależy Ci na tyle, żeby je naprawić.
To ten sam argument co w tekście katy kontra proces wytwórczy, tylko piętro niżej: git jest częścią procesu, a procesu uczysz się, przechodząc go — na początku koślawo — na prawdziwych zmianach.
Gdzie w tym ProdQuest — bez naciągania
Już dziś prawdziwe polecenia gita działają w terminalu w przeglądarce od pierwszej minuty, obok edytora i Pythona, bez instalacji. Czego jeszcze nie ma: pełnego cyklu — repozytorium projektu, pull requestów, agenta recenzującego Twój PR tak, jak zrobiłby to senior, pipeline'ów CI/CD. To tryb process, późniejszy etap publicznej roadmapy, i przejdziemy do niego dopiero wtedy, gdy dane z wcześniejszych etapów pokażą, że działają.
Nie musisz na to czekać. Wszystko powyżej zadziała na dowolnym komputerze z gitem i darmowym kontem na GitHubie. Weź najmniejszy błąd, jaki masz, i zacznij od założenia brancha. A jeśli wolisz ćwiczyć tam, gdzie terminal jest już otwarty, a zadania dobrane do Twojego poziomu — rozwiąż pierwsze zadanie jako gość.