Liniowa historia w repozytorium GITa

System kontroli wersji GIT jest bardzo wdzięcznym i pozwalającym na dużą swobodę narzędziem do przechowywania zmian w naszym kodzie. Każdy kto korzysta z niego na codzień zapewne przekonał się o tym jak korzystanie z GITa może uratować tyłek, a ilość funkcji pozwala wyjść z, wydawałoby się, fatalnej sytuacji podczas pracy w większym zespole. Jednak pracując wg standardowego schematu, który proponuje GIT dochodzimy do momentu kiedy historia zmian w repozytorium zaczyna przypominać spagetti. Nie jest to nic dziwnego, w końcu co chwilę dochodzi nowa gałąź dla nowej funkcjonalności, robione są jakieś quickfixy, ludzie łączą swoje zmiany itd.

Jednak w obecnej pracy poznałem metodę na to jak utrzymać w repozytorium liniową historię zmian.

Zapytasz pewnie po co Ci jest liniowa historia w repozytorium. Już odpowiadam. Po pierwsze fajnie, czysto wygląda. Niby nic nadzwyczajnego i mało użytecznego jednak, np. mnie, nawet drobne oznaki porządku w projekcie cieszą i chociaż minimalnie motywują. Druga sprawa, ważniejsza, to to, że jasno widać jak kod się zmienia. Mamy jedną linię, która wygląda jak oś czasu i od razu widzimy kto w jakim momencie wrzucił swoje zmiany do repozytorium. Tworząc liniową historię pozbywamy się też commitów mergujących przez co w głównej gałęzi mamy tylko to co faktycznie jest zmianami w projekcie, nie ma śmieci w postaci commitów z rozwiązywania konfliktów.

Sam przychodząc do obecnej pracy nie miałem raczej doświadczenia w pracy z GITem. Przerażało mnie już samo commitowanie, a tu jeszcze wymagają ode mnie utrzymywania liniowej historii repo. Jednak już po szybkim wytłumaczeniu całego algorytmu i kilku próbach można powiedzieć, że w ciągu tygodnia stało się to dla mnie w miarę naturalne. I właśnie w tym wpisie podzielę się z Wami tym „algorytmem” bo uważam, że jest dosyć przystępny. Jestem też świadomy, że nie mówię tutaj o niczym innowacyjnym i pewnie prędzej czy później uda się każdemu znaleźć podobny schemat w innych miejscach internetu. Przynajmniej zaoszczędzę Wam szukania ;)

Mimo, że po 4 miesiącach pracy z tym systemem cała procedura stała się dla mnie raczej automatyczna to jednak cały czas na firmowym komputerze mam gdzieś ją zapisaną w pliku txt. To samo polecam Wam na początku. Lepiej jest posiłkować się ściągą niż coś sobie zepsuć „bo wydawało mi się, że tak to leciało”.

No dobra, po tym przydługim wstępie pora na konkrety. Zakładam, że znacie podstawowe komendy systemu Git: pull, push, checkout, add, commit, rebase, merge. Chociaż akurat jeśli chodzi o add i commit to osobiście w pracy używam do tego SourceTree i po prostu wyklikuję commita z komentarzem. Mówiąc „scommituj zmiany” będę miał na myśli zrobienie „add” i potem „commit”. Jeśli chodzi o stan wyjściowy repozytorium to zacznijmy od najprostszej postaci czyli mamy gałąź ‚master’ połączoną ze zdalną gałęzią ‚origin/master’ na serwerze. No więc przychodzimy pierwszy raz do pracy, ściągamy zawartość repozytorium z gałęzi master na nasz komputer.

Pierwsza rzecz jaką robimy to dodajemy nową gałąź. Ja ją nazwę ‚wip’. Robimy to komendą

W tym momencie jesteśmy na gałęzi ‚wip’. I ta gałąź będzie właśnie tą, na której będziemy wprowadzać swoje zmiany. Po utworzeniu gałęzi jej zawartość jest kopią gałęzi, z której przyszliśmy.

Pracujemy radośnie nad kodem. Dodajemy lokalne commity i w końcu nadchodzi moment kiedy musimy to wszystko wrzuci na serwer, tak żeby inni też mogli korzystać z owoców naszej pracy.

Mamy kilka nowych commitów na gałęzi ‚wip’. Przechodzimy więc poleceniem  git checkout master  na gałąź ‚master’. Wykonujemy polecenie  git pull  w celu pobrania ze zdalnego serwera zmian dokonanych przez kolegów.

W tym momencie zaczyna się magia rebasowania. Mając pobrane zmiany dla gałęzi ‚master’ ponownie przechodzimy na gałąź ‚wip’ poleceniem  git checkout wip . Aby połączyć własne i cudze zmiany wykonujemy teraz polecenie, które jest właściwie esencją tej dyskusji:

Powoduje ono, że wszystkie nowe commity z gałęzi master są dodawane do gałęzi ‚wip’ a na nie nakładane są nasze lokalne commity nawet jeśli pierwotnie były robione pomiędzy zmianami innych.

Zakładając, że nie ma konfliktów (mogą się czasami pojawić jednak sposób ich eliminacji zależy od tego czy robimy to zewnętrznym narzędziem czy ręcznie edytujemy itd.) ponownie wracamy na gałąź ‚master’.

Teraz dla pewności wykonujemy ponownie  git pull  w razie jakby ktoś dodał commit kiedy my przenosiliśmy zmiany na gałąź ‚wip’. Jeśli nowe zmiany się pojawiły to poprzednią procedurę z poleceniem rebase ponawiamy. Jeśli zmian nie ma to idziemy dalej. Poleceniem:

Przenosimy do mastera zmiany z naszej deweloperskiej gałęzi ‚wip’. Jeśli pracujemy w konsoli i wszystko poszło ok to powinniśmy zobaczyć, że nasz merge był typu Fast-Forward a więc tylko dopisał nowe zmiany na samej górze historii.

Nie pozostaje nam nic innego jak natychmiast wrzucić zmiany na serwer poleceniem:

I od tego momentu wszyscy mogą pobrać kod, który dodaliśmy a historia zmian w naszym repozytorium jest całkowicie liniowa i nie zawiera żadnych commitów mergujących, które zaciemniają listę zmian.

Niezależnie od przyjętej metody nieuniknione jest użycie polecenia merge jednak powyżej pokazany sposób powoduje, że merge jest tylko formalnością i nie dodaje śmieci do naszej historii. Ważne to jest np. kiedy korzystacie z automatycznych buildów i w razie błędu kompilacji czy po uruchomieniu testów widzicie co faktycznie zostało zrobione w ostatnim czasie. Nie musicie przebijać się w gąszczu commitów typu „Merge branches ‚wip’ and ‚master'”

Jestem świadomy, że bez obrazków może być na początku trudno sobie wyobrazić jak to wygląda, ale zdecydowanie polecam popróbować samemu wykonywać te polecenia. Nawet jeśli nie pracujecie w grupie, ani nie macie dwóch komputerów możliwe jest zrobienie tego sztucznie, a więc mając kopie repozytorium w dwóch folderach, z których commitujecie niezależnie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *