Pytanie „merge czy rebase?” to jedno z najczęstszych w zespołach używających Gita. Obie strategie osiągają ten sam efekt – integrację zmian – ale produkują zupełnie inną historię. Nie ma jednej właściwej odpowiedzi, jest kontekst. Pokażę kiedy co stosować, jak działają konflikty, i dlaczego fast-forward to nie to samo co „bez merge commita”.
Trzy strategie merge
Fast-forward
Możliwy gdy branch docelowy nie divergował od momentu odgałęzienia feature brancha. Git po prostu przesuwa wskaźnik – nie powstaje żaden merge commit.
# Fast-forward jest domyślny gdy możliwy git merge feature/new-endpoint # Wymuś brak fast-forward (zawsze twórz merge commit) git merge --no-ff feature/new-endpoint # Sprawdź czy możliwy FF przed merge git merge-base --is-ancestor main feature/new-endpoint echo $? # 0 = tak, 1 = nie
Merge commit
Tworzy nowy commit z dwoma rodzicami. Zachowuje pełną historię – widać kiedy branch powstał i kiedy wrócił. Preferowany w Git Flow na merge do main/develop.
git checkout main git merge --no-ff feature/order-export # Wiadomość merge commita # Merge branch 'feature/order-export' # # * feat(export): add CSV export for orders # * feat(export): add PDF export for orders # * test(export): add unit tests for ExportService
Rebase przed merge
Feature branch jest „rebazowany” na aktualny main, a następnie fast-forward mergowany. Historia wygląda liniowo – jakby feature był rozwijany na aktualnej wersji maina.
git checkout feature/order-export git rebase main # przenieś commity na wierzchołek main git checkout main git merge feature/order-export # fast-forward
Konflikty – jak je rozwiązywać
# Podczas merge/rebase Git zatrzymuje się na konflikcie: # CONFLICT (content): Merge conflict in src/Model/OrderService.php # Sprawdź co jest w konflikcie git status git diff --check # Plik z konfliktem zawiera markery: # <<<<<<< HEAD # $this->calculateTax($order); # ======= # $this->calculateVat($order, $storeId); # >>>>>>> feature/tax-refactor # Rozwiąż ręcznie, usuń markery, a potem: git add src/Model/OrderService.php git merge --continue # lub git rebase --continue # Porzuć merge/rebase git merge --abort git rebase --abort
Narzędzia do rozwiązywania konfliktów
# Wbudowane narzędzie (otwiera 3-panelowy widok) git mergetool # Konfiguracja narzędzia git config --global merge.tool vimdiff git config --global merge.tool phpstorm # PhpStorm rozwiązuje konflikty wygodnie przez File | Git | Resolve Conflicts
Kiedy merge, kiedy rebase
| Sytuacja | Strategia | Powód |
|---|---|---|
| Feature branch na main (Git Flow) | –no-ff merge | Widoczna historia feature |
| Aktualizacja feature z main | rebase | Liniowa historia, prostsze konflikty |
| Hotfix na main | fast-forward | Prosta zmiana, bez szumu |
| Squash przed merge PR | squash + merge | Jeden commit per PR |
| Publiczny branch (shared) | merge NIGDY rebase | Rebase przepisuje SHA |
Octopus merge – scalanie wielu branchów
# Merge wielu branchów jednocześnie (octopus merge) git merge feature-a feature-b feature-c # Przydatne przy scalaniu niezależnych feature branchów # Git wymaga braku konfliktów między nimi
Podsumowanie
Merge commit zachowuje historię rozgałęzień ale „zanieczyszcza” log merge commitami. Rebase tworzy liniową historię ale przepisuje SHA – nie wolno go stosować na publicznych branchach. W praktyce: rebase do aktualizacji feature brancha, –no-ff merge do integracji na główny branch. Następny wpis: praca zdalna – remote, fetch vs pull, force-with-lease.
