Coraz więcej osób korzysta dzisiaj z GIT, zamiast starych i scentralizowanych repozytoriów. Czasami jednak mnogość komend może być przytłaczająca, zwłaszcza na początku. Dzisiaj chciałbym wyjaśnić czym różni się Merge od Rebase.
Obie komendy służą do tego samego czyli synchronizacji dwóch gałęzi kodu np. lokalnego repozytorium ze zdalną gałęzią. Efekt na końcu będzie “taki sam”, czyli połączenie lokalnego kodu, ze zmianami naniesionymi np. w gałęzi. Dlaczego zatem GIT tak to “komplikuje”? W TFS czy w SVN zwykle korzystało się z komendy typu Update i to wszystko. W GIT jest trochę inaczej i zwykle korzysta się np. z Fetch+Merge, Fetch+Rebase lub po prostu PULL.
Najlepiej pokazać to na przykładzie. Załóżmy, że mamy pewną gałąź, np. master:
Powyższy rysunek przedstawia 3 zmiany (commit). Następnie użytkownik tworzy gałąź, w celu zaimplementowania czegoś (feature branch) i dokonuje również kilku zmian:
Bardzo możliwe, że w tym samym czasie ktoś zmodyfikował master:
Po jakimś czasie będziemy musieli zintegrować Branch A z master. Mamy do wyboru Merge albo Rebase.
Jeśli użyjemy klasyczny merge (branch->master), wtedy struktura będzie wyglądać następująco:
Komenda tworzy dodatkowy commit, który scala dwie gałęzie. W historii zmian, zobaczymy coś w stylu “merge commit”, który scala je. Powstanie zatem rozwidlienie i historia zawartości nie będzie liniowa jak to widać na powyższym rysunku. Zaletą merge jest prostota bo nie modyfikujemy poprzedniego kodu – po prostu tworzymy na samej górze dodatkowy commit, który zakańcza rozwidlenie gałęzi. Wadą rozwiązania jest fakt, że przy każdym merge, powstaje dodatkowy commit, co zwykle utrudnia przeglądanie historii.
Rebase z kolei, spowoduje, że będziemy mieli bardzo przejrzystą historie zmian:
Widzimy, że struktura jest liniowa. Rebase jak sama nazwa sugeruje, polega na zamianie podstawy. W tym przypadku, commity’ z gałęzi “branch a” zostały przepisane u podstawy master. Efekt jest taki, że nie musimy tworzyć sztucznego “merge commit” i w historii zmian widzimy tylko to, co faktycznie zostało zmodyfikowane przez innych użytkowników.
Innymi słowy, Rebase tworzy liniową strukturę, ale modyfikuje historie zmian, z kolei Merge tworzy dodatkowy commit, scalający rozwidlenia, ale powoduje to powstanie nieliniowej struktury zmian.
Struktura liniowa nie jest jednak zawsze pożądana – w końcu rozwidlenie w przypadku Merge dostarcza jakąś informację o kontekście zmian. W przypadku Rebase nie wiadomo, że feature branch został scalony i stąd nastąpiły zmiany. Ponadto, ze względu, że rebase dokonuje przepisania commitów może być to czasami niebezpieczne – w końcu jest to ingerencja w już zapisany kod.
W przyszłym poście postaram się napisać kilka wskazówek dotyczących wyboru między Rebase a Merge oraz czego należy unikać (oczywiście z mojego punktu widzenia).
Dzięki za ten post. Bardzo przydatny dobrze opisany. Czekam na kolejny post kiedy której komendy używać.
Dla początkujących przydałoby się jeszcze napisanie na której gałęzi robimy checkout i rebase/merge.
Czy to powinno być:
$git checkout master
$git merge my-feature
czy też
$git checkout my-feature
$git merge master
Jesli chcemy zmiany B na galezi A czyli B->A, wtedy robimy obie komendy na A. Zawsze odpalamy je na galeziach DO ktorych chcemy doczepic jakies zmiany.
Artykuł bardzo potrzebny bo wiele osób nie czuje tej różnicy, natomiast z moejgo doświadczenia wynika, że rebease to największe zło jakie mnie spotkało w historii.
Wszystko jest wspaniale dopóki nie potrzebujemy się cofnąć w repo. Po rebease historia repo nie jest taka sama, więc po cofnięciu nie mamy gwarancji, że wrócimy do takiego świata jaki mamy na myśli (bo może ktoś go już zmienił stosując rebease i część commitów poszła na górę).
Osobiście uważam, że zrobienie feature branch z głównej linii i podejści zgodne z git flow ma najlepsze zastosowanie. Nie dokłada nie wiadomo ile pracy a już neiraz przekonałam się, że taki dodatkowy branch ratuje życie, jesli nie powiedzie się merge innej osoby i nadpisze nasze zmiany lub środowisko sie rozjedzie (zawsze mamy kopie na tym feature branchu.
Czekam na dalszą część artykułu, który rozpatrzy więcej specyficznych przypadków 🙂
Bardzo pomocny artykuł, dzięki 😉
super wytłumaczone !
Na ostatnim obrazku (po rebase) jest:
1 -> 2 -> 3 -> 4b -> 5b -> 6b -> 4 -> 5
Czy nie powinno być:
1 -> 2 -> 3 -> 4 -> 5 -> 4b -> 5b -> 6b
?
Na ostatnim obrazku (po rebase) jest:
1 -> 2 -> 3 -> 4b -> 5b -> 6b -> 4 -> 5
Czy nie powinno być:
1 -> 2 -> 3 -> 4 -> 5 -> 4b -> 5b -> 6b
?
Na ostatnim obrazku (po rebase) jest:
1 -> 2 -> 3 -> 4b -> 5b -> 6b -> 4 -> 5
Czy nie powinno być:
1 -> 2 -> 3 -> 4 -> 5 -> 4b -> 5b -> 6b
?