[GIT] Zaawansowane funkcje – cz. 2 – Mediacje trójstronne

GIT MERGE

Drugi artykuł w mini cyklu o GIT. W dzisiejszym odcinku powiemy o podstawach, właściwie to o najbardziej podstawowej funkcji GITa, czyli łączeniu zmian z różnych gałęzi.

Nie będziemy zajmować się trywialnymi zagadnieniami w stylu „jak to zrobić”, przyjrzymy się natomiast dwóm kwestią: fast-forward i merge-commit. Co to właściwie jest i w jakich sytuacjach wystarczy przesunięcie głowy, a jakich jest konieczne wykonanie dedykowanego commitu łączącego zmiany z dwóch gałęzi.
Dobre zrozumienie tej podstawy jest konieczne aby pojąć co dzieje się „pod maską” polecenia Rebase.

Seria Artykułów o GIT – spis treści
  1. GIT HEAD – co to jest? Detached Head. Kiedy można stracić głowę?
  2. GIT fast-forward i Merge Commit. Co to właściwie jest?
  3. Git REBASE i Squash. Tworzymy ładną historię zmian.
  4. Przydatne narzędzia i oprogramowanie do pracy z GIT
  5. Przydatne Materiały o GIT plus BONUS – ściąga z poleceń GIT’a
  6. Podsumowanie.

Merge commit i fast forward

Zanim dojdziemy do rebase, powinniśmy przypomnieć sobie co oznacza fast-forward i czym jest merge-commit.

Fast-forward

Jeśli gałąź, którą chcemy dołączyć wywodzi się bezpośrednio z gałęzi, do której dołączamy i po drodze nie ma żadnych innych zmian – realizowany jest fast-forward.
Innymi słowy: mamy gałąź master, wychodzimy z niej branchem hot-fix, w branchu hot-fix wykonujemy dwa commity po czym chcemy dołączyć hot-fix do master, wykonujemy więc:

git checkout master
git merge hot_fix

Jeśli w między czasie w gałęzi master nie wykonany żadnych zmian to podczas złączania (merge) GIT wykona fast-forward, czyli zwyczajnie przeniesie wskaźnik master na miejsce, na które wskazuje wskaźnik hot_fix. Fizycznie nie jest realizowane żadne scalenie plików, a tylko przeniesienie wskaźnika. Fast forward daje nam liniową historię i nie wprowadza dodatkowego commita połączeniowego (merge-commit).
Gałąź hot-fix można usunąć gdyż master wskazuje dokładnie na ten sam commit. Proste, prawda? 🙂

Merge-commit

Jeśli historia zawiera dodatkowe zmiany pomiędzy miejsce, „z którego wyszliśmy naszym commitem”, a miejscem „do którego dołączamy gałąź” realizowane jest scalanie trójstronne i powstaje merge commit. Po kolei:

Przed scalaniem mamy następującą sytuacje: wyszliśmy branchem some-feature z brancha main, zrobiliśmy na nim dwa commity. W międzyczasie na branchu main nasz kolega z zespołu wprowadził dodatkową zmianę, która nie jest uwzględniona w naszej gałęzi some-feature.
Konieczne jest więc scalenie zmian.

Scalanie Trójstronne – stan przed scaleniem

Takie scalanie nazywa się scalaniem trójstronnym, ponieważ biorą w nim udział trzy strony:

  • wspólny przodek obu gałęzi, czyli miejsce, „z którego wyszliśmy”,
  • nasz aktualny stan (czyli aktualny stan gałęzi some-feature),
  • stan gałęzi, do której dołączamy, czyli aktualny stan gałęzi main.

Wniosek?

Merge commit nie jest wykonywany gdy wystarczy fast-forward! A co jeśli z jakiegoś powodu, np. ze względu na ustalenia projektowe chcemy wykonać merge-commit w sytuacji gdy wystarczyłoby proste przesunięcie HEAD (fast-forward)?
Mamy do tego specjalną komendę:

git merge --no-ff <branch>

Rzecz o konfliktach

Nieodłącznym elementem wspólnej pracy wielu programistów są konflikty, także konflikty w kodzie! 🙂 Jeśli zarówno my jak i inny członek zespoóu zmodyfikował dany plik najczęściej GIT nie będzie wiedziałjak dokonać połączenia naszych zmian. Oczywiście do rozwiązywania konfliktów możemy wykorzystać kilka bardzo dobrych narzędzi, o narzędziach będzie w oddzielnym wpisie! Jednak GIT ma swój wbudowany sposób na rozwiązywanie konfliktów: GITwprowadza znaczniki w pliku dotkniętym konfliktem, wygląda to mniej więcej tak:

here is some content not affected by the conflict
<<<<<<< main
this is conflicted text from main
=======
this is conflicted text from feature branch
>>>>>>> feature branch;
  • Przed znakami <<< znajduje się część kodu źródłowego, w której nie ma konfliktu,
  • pomiędzy znakami <<< nazwa_gałęzi do której dołączamy zmiany, a znakami „====” znajduje się część dotknięta konfliktem pochodząca z gałęzi do której dołączamy zmiany (cel)
  • pomiędzy znakami „===”, a „>>>” znajduje się fragment „naszego kodu”, czyli kodu, który chcemy dołączyć do gałęzi „głównej” (źródło).
  • Git tworzy plik o powyższej strukturze, po naszej stronie jest odpowiednie jego skorygowanie oraz wykonanie commitu zmian.

To tyle jeśli chodzi o przypomnienie bazowej wiedzy.

P.S. Teraz już wiesz skąd bierze się tłumaczenie programistów „faktycznie, buildy nie idą, ale GIT mi sam wstawił te znaczki <<<< i teraz się nie kompiluje”… 🙂

W kolejnym artykule szerzej przyjrzymy się REBASE i Squash. Zapraszam!

4 odpowiedzi na “[GIT] Zaawansowane funkcje – cz. 2 – Mediacje trójstronne”

  1. W końcu zrozumiałem co znaczy gdy GIT podczas REBEASE i rozwiązywania konfliktów wyświetla info o „missing HEAD”. Dzieki!

Leave a Reply