Merge — to operacja scalania gałęzi w Git, która łączy zmiany z dwóch różnych linii rozwoju w jedną docelową gałąź. W przeciwieństwie do rebase, merge zachowuje pełną historię rozgałęzień, tworząc specjalny commit scalania z dwoma rodzicami. Według oficjalnej dokumentacji Git (2026), merge jest najbezpieczniejszym sposobem łączenia gałęzi, ponieważ nie nadpisuje historii i pozwala śledzić, kiedy i które gałęzie zostały scalone. Jest to standardowy wybór do scalania w publicznych gałęziach, takich jak main, develop i release.
Najważniejsze
Merge — to komenda git merge, która łączy zmiany z określonej gałęzi do bieżącej. Git znajduje wspólnego przodka (wspólny commit bazowy), oblicza diff każdej gałęzi względem przodka i tworzy commit scalania zawierający połączony zestaw zmian. Rezultat — gałąź docelowa zostaje uzupełniona wszystkimi zmianami ze scalanej gałęzi.
Składnia: będąc w gałęzi docelowej (np. main), wykonaj git merge feature. Git automatycznie tworzy commit scalania, jeśli nie ma konfliktów. W domyślnej wiadomości commita scalania podawane jest: „Merge branch 'feature' into main". Wiadomość można zmienić przez flagę -m lub edytować w otwartym edytorze.
Merge to operacja nieniszcząca. W przeciwieństwie do rebase, merge nie rusza istniejących commitów: pozostają one z tymi samymi hashami, autorami i datami. To czyni merge jedynym bezpiecznym sposobem scalania dla gałęzi, nad którymi jednocześnie pracuje wielu programistów. Jeśli coś pójdzie nie tak, merge można anulować komendą git merge --abort.
# Przełącz na gałąź docelową
git checkout main
# Scal gałąź funkcji
git merge feature
# Wynik — commit scalania z dwoma rodzicami
git log --oneline --graph
# Scal z własną wiadomością
git merge feature -m "feat: integrate authentication module"
Git obsługuje trzy tryby scalania, które wybiera się w zależności od pożądanego rezultatu. Regular merge (domyślny) tworzy commit scalania. Squash merge łączy wszystkie commity gałęzi funkcji w jeden. Fast-forward — przesuwa wskaźnik gałęzi bez tworzenia commita, jeśli to możliwe. Wybór trybu zależy od workflow zespołu i zasad historii.
Regular merge (--no-ff) — tworzy commit scalania nawet jeśli scalenie można wykonać jako fast-forward. Zalecany dla gałęzi main: commit scalania wyraźnie oznacza moment integracji funkcji i pozwala łatwo wycofać wszystkie zmiany gałęzi funkcji przez jeden revert commita scalania. GitHub domyślnie używa tego trybu przy scalaniu PR przez przycisk Merge.
Squash merge (--squash) — zbiera wszystkie commity gałęzi funkcji w jeden commit w gałęzi docelowej. Przydatny, gdy brudna historia gałęzi funkcji nie powinna trafić do main. Wadą jest utrata połączenia z oryginalnymi commitami — nie można zobaczyć, jak funkcja była rozwijana krok po kroku. GitHub używa tego trybu przy wyborze „Squash and merge" w PR.
Fast-forward (--ff) — jeśli gałąź docelowa nie ma nowych commitów po odgałęzieniu funkcji, Git po prostu przesuwa wskaźnik do przodu, bez tworzenia commita scalania. Historia pozostaje liniowa. Flaga --no-ff wymusza utworzenie commita scalania, --ff-only zakończy się błędem, jeśli fast-forward nie jest możliwy.
# Wymuś commit scalania (zalecane dla main)
git merge --no-ff feature
# Scalenie squash — wszystkie commity w jeden
git merge --squash feature
git commit -m "feat: add authentication"
# Fast-forward tylko jeśli możliwe
git merge --ff-only feature
# Przerwij skonfliktowane scalenie
git merge --abort
Strategie scalania określają algorytm, którego Git używa do łączenia zmian. Każda strategia jest odpowiednia do różnych scenariuszy. Git automatycznie wybiera odpowiednią strategię, ale programista może wskazać ją jawnie przez flagę --strategy. Zrozumienie strategii pomaga przewidzieć zachowanie Git przy złożonych scaleniach.
Recursive — domyślna strategia do scalania dwóch gałęzi. Git znajduje wspólnego przodka, oblicza zmiany w każdej gałęzi i łączy je. Jeśli wspólny przodek został znaleziony, recursive poprawnie obsługuje zmiany nazw plików i dodawanie nowych. Przy konfliktach recursive może używać dodatkowych opcji: ours (automatycznie wybiera naszą wersję) i theirs (wybiera ich wersję).
Octopus — do jednoczesnego scalania więcej niż dwóch gałęzi: git merge feature1 feature2 feature3. Octopus nie obsługuje rozwiązywania konfliktów — wszystkie konflikty muszą być rozwiązane przed wywołaniem komendy. Używany rzadko, głównie do łączenia kilku niezależnych gałęzi, które gwarantowanie nie kolidują (np. różne moduły).
| Strategia | Liczba gałęzi | Rozwiązywanie konfliktów |
|---|---|---|
| Recursive | 2 | Automatyczne + opcje ours/theirs |
| Octopus | 3+ | Nie — wszystkie konflikty muszą być rozwiązane z góry |
| Ours | Dowolna | Zawsze wybiera naszą wersję, obce zmiany są ignorowane |
| Subtree | 2 | Do scalania poddrzew (subtree merge) |
Ours — szczególna strategia, która całkowicie ignoruje zmiany ze scalanej gałęzi i zachowuje bieżącą zawartość gałęzi docelowej. Commit scalania jest tworzony, ale zawartość pozostaje bez zmian. Przydatna, gdy trzeba utrwalić w historii fakt scalenia, ale faktycznie odrzucić wszystkie zmiany z obcej gałęzi.
Konflikt merge powstaje, gdy te same wiersze pliku zostały zmienione w obu gałęziach w różny sposób. Git nie może automatycznie określić, która wersja jest poprawna, i wstrzymuje merge. Konflikt może również powstać przy zmianie nazwy pliku w jednej gałęzi i jego modyfikacji w drugiej, lub przy jednoczesnym usunięciu i modyfikacji tego samego pliku.
Proces rozwiązywania: Git oznacza konfliktowe pliki markerami. W pliku pojawiają się fragmenty z <<<<<<< HEAD (nasza wersja), ======= (separator) i >>>>>>> feature (ich wersja). Programista ręcznie edytuje konfliktowy fragment, wybierając potrzebne wiersze z obu wersji, usuwa markery, zapisuje plik i dodaje go do indeksu przez git add.
Do wizualnego rozwiązywania konfliktów Git obsługuje mergetool — zewnętrzne narzędzie porównawcze. Popularne narzędzia mergetool: Meld, KDiff3, Beyond Compare, VS Code (wbudowany edytor konfliktów). Mergetool wyświetla trzy panele: naszą wersję, ich wersję i wynik. Programista wizualnie wybiera bloki kodu do włączenia do pliku końcowego.
# Rozpocznij scalanie i wykryj konflikt
git merge feature
# KONFLIKT (treść): Konflikt scalania w src/main.swift
# Sprawdź skonfliktowane pliki
git status
# Otwórz wizualne narzędzie scalania
git mergetool
# Po rozwiązaniu — dodaj i zatwierdź
git add src/main.swift
git commit
# Przerwij scalanie
git merge --abort
Merge jest preferowany nad rebase w kilku kluczowych sytuacjach. Pierwsza: podczas pracy z publicznymi gałęziami dostępnymi dla innych programistów. Merge nie nadpisuje historii, a współpracownicy mogą bezpiecznie synchronizować się. Rebase w publicznej gałęzi stworzy rozbieżną historię i konflikty u wszystkich, którzy już otrzymali stare commity.
Druga sytuacja: przy kończeniu gałęzi funkcji. Większość zespołów preferuje merge (z flagą --no-ff) do main, aby utrwalić moment integracji funkcji. Upraszcza to nawigację po historii i pozwala łatwo wycofać całą funkcję jednym git revert commita scalania. GitHub Flow domyślnie oferuje trzy opcje merge: prosty merge, squash merge i rebase merge.
Trzecia sytuacja: przy pracy z pull requestem, który przeszedł przegląd. GitHub i GitLab oferują przycisk merge z różnymi opcjami. Merge (Create a merge commit) — pełna historia z commitem scalania. Squash and merge — czysta historia bez szczegółów rozwoju. Rebase and merge — liniowa historia bez commita scalania, ale z nadpisaniem commitów. Wybór zależy od zasad zespołu.
Pierwsza zasada: zawsze być na aktualnej wersji gałęzi docelowej przed merge. Wykonaj git checkout main && git pull zanim scalisz funkcję. Minimalizuje to konflikty i gwarantuje, że commit scalania będzie zawierał wszystkie aktualne zmiany. Jeśli gałąź docelowa znacznie wyprzedziła, najpierw wykonaj git merge main w gałęzi funkcji, aby rozwiązać konflikty w jej kontekście.
Druga zasada: testować kod po merge. Merge może zmienić zachowanie, nawet jeśli nie było konfliktów. Pipeline CI/CD powinien przeprowadzić testy na commicie scalania przed wysłaniem do produkcji. Niektóre zespoły używają merge gates — obowiązkowych sprawdzeń, które blokują merge do czasu ich przejścia.
Trzecia zasada: dokumentować commity scalania. Standardowa wiadomość „Merge branch 'feature' into main" jest mało przydatna. Zaleca się dodawanie opisu tego, co zostało scalone: „Merge authentication module: login, registration, password recovery". Upraszcza to analizę historii i wyszukiwanie regresji. W dużych projektach commity scalania są automatycznie generowane z nazwy PR.
Często zadawane pytania
Łączyć (merge) — wykonać git merge w celu połączenia zmian z jednej gałęzi do drugiej. Rezultatem jest commit scalania, który utrwala fakt scalenia i zawiera zmiany z obu gałęzi. Jest to podstawowy sposób integracji gałęzi funkcji z main, develop lub release w Git Flow.
Squash merge łączy wszystkie commity gałęzi funkcji w jeden commit w gałęzi docelowej, tracąc pośrednią historię rozwoju. Zwykły merge tworzy commit scalania, zachowując wszystkie commity gałęzi funkcji. Squash merge daje czystą historię, ale nie pozwala śledzić krokowego rozwoju funkcji.
Otwórz konfliktowy plik, znajdź fragmenty z markerami <<<<<<< HEAD i >>>>>>>. Edytuj treść, zostawiając potrzebne wiersze z obu wersji, usuń markery. Zapisz plik, wykonaj git add i git commit. Możesz użyć git mergetool do wizualnego rozwiązywania.
Merge jest zawsze używany dla publicznych gałęzi (main, develop, release), ponieważ nie nadpisuje historii. Rebase stosuje się w prywatnych gałęziach funkcji przed ich publikacją. Po tym, jak gałąź stała się częścią wspólnego repozytorium i inni programiści się do niej odwołali, dozwolony jest tylko merge.
Przed zakończeniem merge (podczas konfliktu) — git merge --abort anuluje scalanie całkowicie. Po zakończeniu — git revert <merge-commit-hash> -m 1 tworzy commit cofający. Flaga -m 1 wskazuje, którą gałąź nadrzędną zachować (docelową). Git revert jest bezpieczniejszy niż git reset dla opublikowanych gałęzi.
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ż