Merge — to operacja w Git, która łączy zmiany z jednej gałęzi do drugiej, tworząc commit scalania (merge commit). Git obsługuje kilka strategii: fast-forward (liniowa historia), three-way merge (z utworzeniem merge commit) i squash merge (skompresowanie wszystkich commitów w jeden). Według danych git-scm.com, 2025, merge pozostaje najczęściej używanym mechanizmem integracji kodu w zespołowej pracy z Git.
Najważniejsze
Merge (scalanie) — to fundamentalna operacja w Git, która łączy zmiany z jednej gałęzi (source) do drugiej (target). W wyniku scalania gałąź docelowa otrzymuje wszystkie commity z gałęzi źródłowej, których jeszcze w niej nie było. W zależności od sytuacji Git może wykonać merge na trzy różne sposoby.
Główną wartością merge jest zachowanie historii: merge commit rejestruje fakt połączenia gałęzi, przechowuje informację o tym, kiedy i które gałęzie zostały scalone. Ułatwia to audyt zmian, wyszukiwanie regresji i zrozumienie chronologii rozwoju. W dużych projektach merge commit jest standardowym sposobem integracji kodu.
Według danych GitLab Flow, merge commity są używane w 73% zespołów pracujących z Git. Alternatywne podejścia (rebase, squash) preferują zespoły nastawione na liniową historię. Wybór strategii zależy od wielkości zespołu, częstotliwości wydań i przyjętych w projekcie ustaleń.
Merge jest wymagany, gdy programista zakończył pracę nad funkcją i chce zintegrować ją z develop lub main. Typowy scenariusz: programista utworzył gałąź funkcji od develop, pracował w niej kilka dni, a w tym czasie w develop pojawiły się nowe commity od innych uczestników. Przed scalaniem trzeba połączyć zmiany — i do tego służy merge.
Bez merge niemożliwa jest wspólna praca nad jednym kodem w Git. Za każdym razem, gdy dwóch programistów jednocześnie wprowadza zmiany do tej samej bazy kodu, ich gałęzie się rozchodzą. Merge — to jedyny sposób, aby połączyć te zmiany z powrotem bez utraty danych.
Git obsługuje trzy typy merge, z których każdy jest przeznaczony do swojego scenariusza. Wybór typu scalania wpływa na historię commitów, wygodę wycofywania i czytelność logu.
Fast-forward występuje, gdy gałąź docelowa nie miała nowych commitów od momentu utworzenia źródłowej. W tym przypadku Git po prostu przesuwa wskaźnik gałęzi docelowej do przodu, na ostatni commit źródłowej. Historia pozostaje liniowa, bez merge commitu.
# Fast-forward merge: develop nie zmieniał się od momentu utworzenia feature
git checkout develop
git merge feature/new-login
# Wynik: wskaźnik develop przesunął się na koniec feature
# Nie utworzono żadnego merge commitu
Fast-forward jest wygodny dla krótko żyjących gałęzi, gdzie programista pracował samodzielnie. Ale to podejście ma wadę: traci się informację o tym, że gałąź istniała — wszystkie commity wyglądają jak zrobione bezpośrednio w develop.
Three-way merge jest wykonywany, gdy obie gałęzie mają nowe commity po punkcie rozbieżności. Git tworzy osobny merge commit z dwoma rodzicami, który rejestruje fakt połączenia gałęzi. To podejście jest zalecane dla gałęzi funkcyjnych w pracy zespołowej.
# Wymuszony three-way merge z flagą --no-ff
git checkout develop
git merge --no-ff feature/new-login
# Utworzono merge commit z domyślną wiadomością
# Można ustawić własną wiadomość przez -m
git merge --no-ff feature/new-login -m "Merge feature/new-login into develop"
Flaga --no-ff gwarantuje utworzenie merge commitu, nawet jeśli fast-forward jest możliwy. To najlepsza praktyka dla zachowania informacji o rozgałęzieniach w projekcie.
Squash merge kompresuje wszystkie commity gałęzi źródłowej w jeden i stosuje go do docelowej. Historia funkcji zostaje utracona — do gałęzi trafia jeden commit ze wszystkimi zmianami. Jest to wygodne, gdy szczegółowe commity w gałęzi funkcji nie niosą wartości dla ogólnej historii.
# Squash merge: wszystkie commity feature skompresowane w jeden
git checkout develop
git merge --squash feature/experimental
git commit -m "feat: experimental login flow (squashed)"
Squash nadaje się do wersji roboczych, gałęzi eksperymentalnych i sytuacji, gdy ważne jest zachowanie czystości historii. Minus — traci się związek z oryginalnymi commitami, co utrudnia wycofywanie poszczególnych zmian.
Ours i Theirs — dwie specjalne strategie merge w Git. Ours całkowicie ignoruje zmiany z gałęzi źródłowej, pozostawiając tylko to, co jest w docelowej. Theirs, przeciwnie, przyjmuje wersję gałęzi źródłowej przy każdym konflikcie. Te strategie są przydatne przy scalaniu dużych ilości kodu, gdy z góry wiadomo, która wersja ma zwyciężyć.
Mechanizm merge w Git opiera się na porównaniu trzech punktów: wspólnego przodka (merge base), stanu gałęzi źródłowej i stanu gałęzi docelowej. Git znajduje merge base — ostatni commit wspólny dla obu gałęzi — i oblicza, jakie zmiany zaszły w każdej gałęzi po rozbieżności.
Git używa trójstronnego algorytmu scalania, który uwzględnia nie tylko dwie porównywane wersje pliku, ale także ich wspólnego przodka. Dzięki temu Git może automatycznie rozwiązywać sytuacje, gdy zmiany w jednej gałęzi nie dotyczą zmienionych fragmentów drugiej — nawet jeśli oba pliki zostały zmodyfikowane.
Rozważmy scenariusz: dwóch programistów pracuje nad różnymi plikami w jednej gałęzi funkcji. Pierwszy zmienił LoginActivity.kt, drugi — ProfileFragment.kt. Kiedy łączą swoje zmiany, Git widzi, że zmiany dotyczyły różnych plików, i wykonuje merge automatycznie, bez interwencji człowieka.
Jeśli obaj programiści zmienili LoginActivity.kt, ale w różnych metodach — Git również poradzi sobie automatycznie, łącząc zmiany wiersz po wierszu. Konflikt powstaje tylko wtedy, gdy obaj zmienili te same wiersze lub jeśli jeden usunął kod, który drugi zmienił.
Konflikt merge powstaje, gdy Git nie może automatycznie połączyć zmian, ponieważ obie gałęzie zmodyfikowały te same wiersze w różny sposób. W takim przypadku Git oznacza konfliktowe fragmenty w plikach i oczekuje ręcznego rozwiązania przez programistę.
Konfliktowe fragmenty są oznaczane specjalnymi znacznikami: <<<<<<< HEAD pokazuje kod z gałęzi docelowej, ======= — separator, >>>>>>> source-branch — kod z gałęzi źródłowej. Programista musi ręcznie wybrać, który wariant pozostawić, lub połączyć je.
# 1. Uruchomić merge i zobaczyć konflikt
git merge feature/new-login
# Wynik: CONFLICT (content): Merge conflict in LoginActivity.kt
# 2. Wyświetlić listę plików z konfliktami
git status
# both modified: src/ui/login/LoginActivity.kt
# 3. Rozwiązać konflikt: edytować plik, usunąć znaczniki
# 4. Dodać rozwiązany plik i zakończyć merge
git add src/ui/login/LoginActivity.kt
git merge --continue
# lub: git commit (bez --continue)
Do rozwiązywania konfliktów istnieją narzędzia: git mergetool otwiera wizualny merger (Meld, Beyond Compare, VS Code). Wielu programistów woli rozwiązywać konflikty w IDE — IntelliJ IDEA i Android Studio udostępniają wbudowane narzędzie z trójpanelowym porównaniem, które znacznie upraszcza ten proces.
Porady dotyczące rozwiązywania konfliktów: zawsze rozumiej, co robi każda strona konfliktu, nie usuwaj cudzego kodu bez zrozumienia jego logiki, a jeśli konflikt jest zbyt skomplikowany — zaangażuj autora obu gałęzi do wspólnego rozwiązania.
Wybór między Merge a Rebase — to jedna z najczęstszych decyzji architektonicznych w Git. Oba podejścia łączą zmiany, ale robią to inaczej: merge zachowuje historię rozgałęzień, rebase przepisuje historię, czyniąc ją liniową.
Wiele zespołów stosuje podejście hybrydowe: rebase w celu doprowadzenia gałęzi funkcji do aktualnego stanu develop (git rebase develop), a następnie merge z flagą --no-ff dla zarejestrowania scalania. Daje to czystą historię wewnątrz funkcji i informacyjne punkty scalania na poziomie develop.
Często zadawane pytania
Bez --no-ff Git wykonuje fast-forward merge, jeśli to możliwe — po prostu przesuwa wskaźnik gałęzi. Z --no-ff Git zawsze tworzy merge commit, zachowując informację o rozgałęzieniach. Zalecane dla gałęzi funkcyjnych w pracy zespołowej.
Użyj git mergetool lub wbudowanego narzędzia IDE. Jeśli konflikt dotyczy dziesiątek plików — być może gałęzie zbyt mocno się rozeszły. W takim przypadku warto omówić z zespołem plan scalania, być może podzielić go na kilka etapów.
Tak: git merge --abort anuluje merge, jeśli nie został jeszcze zakończony (konflikt). Jeśli merge już się zakończył — użyj git reset --hard HEAD~1 lub git revert -m 1 <merge-commit> dla bezpiecznego wycofania.
Zalecane dla pracy zespołowej. Merge commit rejestruje fakt scalania, zawiera odniesienia do obu gałęzi i ułatwia zrozumienie historii. Dla prywatnych lub eksperymentalnych gałęzi dopuszczalny jest squash merge lub fast-forward.
Git nie może automatycznie scalać plików binarnych — wybiera jedną z wersji w całości. Dla plików binarnych (obrazy, .aab, .apk) zaleca się minimalizowanie równoległych zmian i używanie Git LFS dla dużych plików.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również