Rozdział 4 z 5

Kurs Git: gałęzie, praca zespołowa, merge i rebase

zweryfikowano 7 września 2026 · 10 min

Szybka odpowiedź

Utwórz gałąź komendą git switch -c nom, popracuj, a potem scal ją przez git merge z poziomu gałęzi docelowej. Przy konflikcie Git wstawia do pliku znaczniki: wpisz poprawną treść, usuń znaczniki, a następnie git add i git commit. git merge --abort w dowolnym momencie cofa wszystko.

Gałąź to równoległa linia rozwoju. Pozwala pracować nad funkcjonalnością bez ruszania stabilnej wersji, a koledze z zespołu robić to samo u siebie. Ten rozdział obejmuje pełny cykl: utworzenie gałęzi, scalenie jej, ręczne rozwiązanie konfliktu i wybór między merge a rebase.

git switch zamiast git checkout

Historycznie git checkout służył do wszystkiego: zmiany gałęzi, utworzenia nowej, przywrócenia pliku, ustawienia się na konkretnym commicie. To nagromadzenie ról jest pierwszym źródłem zamieszania u początkujących i źródłem wypadków u całej reszty.

Git 2.23, wydany w sierpniu 2019 roku, rozdzielił tę komendę na dwie: git switch do przechodzenia między gałęziami i git restore do wycofywania zmian. Przy wydaniu obie były oznaczone jako eksperymentalne, w oficjalnej dokumentacji już nie są. Ten rozdział ich używa i za każdym razem podaje odpowiednik z checkout, który jeszcze długo będziesz spotykać w kursach i na Stack Overflow.

Zamiar Nowoczesna komenda Dawna forma
Zmiana gałęzi git switch nom git checkout nom
Utworzenie gałęzi i przejście na nią git switch -c nom git checkout -b nom
Powrót do poprzedniej gałęzi git switch - git checkout -
Cofnięcie zmian w pliku git restore fichier git checkout -- fichier
Wyjęcie pliku z indeksu git restore --staged fichier git reset HEAD fichier

Utworzenie gałęzi i praca na niej

bash
git switch -c feature/panier
Sortie réelle · git switch -c
Switched to a new branch 'feature/panier'

Gałąź wychodzi od commita, na którym byłeś. Możesz teraz normalnie zmieniać, dodawać i commitować: nic z tego nie pojawi się na main, dopóki tego nie scalisz.

Git nie narzuca żadnej reguły nazewnictwa gałęzi, ale przedrostki feature/, fix/ i hotfix/ to rozpowszechniona konwencja, a interfejsy hostingów grupują je wizualnie.

Żeby zobaczyć, w którym miejscu jesteś:

bash
git branch              # lokalne gałęzie
git branch -vv          # z ostatnim commitem i śledzoną gałęzią zdalną
git branch -a           # lokalne i zdalne
git switch -            # powrót do poprzedniej gałęzi

Żeby opublikować gałąź i pokazać ją zespołowi:

bash
git push -u origin feature/panier

Pobranie gałęzi kolegi z zespołu

W drugą stronę git fetch aktualizuje twoją wiedzę o serwerze, nie zmieniając niczego w twoich gałęziach:

bash
git fetch origin feature/collegue   # konkretna gałąź
git fetch --all                     # wszystkie zdalne repozytoria
git branch -r                       # to, co wiesz o serwerze
Sortie réelle · git branch -r après un fetch
  origin/HEAD -> origin/main
  origin/feature/collegue
  origin/main

Zostaje już tylko przejść na tę gałąź. Git sam tworzy gałąź lokalną i wiąże ją z tą z serwera:

bash
git switch feature/collegue
Sortie réelle · git switch sur une branche distante
branch 'feature/collegue' set up to track 'origin/feature/collegue'.
Switched to a new branch 'feature/collegue'

Kiedy Git odmawia zmiany gałęzi

To jeden z pierwszych błędów, na jakie trafisz:

Sortie réelle · git switch avec des modifications non committées
error: Your local changes to the following files would be overwritten by checkout:
	index.html
Please commit your changes or stash them before you switch branches.
Aborting

Git chroni twoją pracę: zmieniony przez ciebie plik ma inną zawartość na drugiej gałęzi, a przejście na nią nadpisałoby go. Są trzy wyjścia, w kolejności od najlepszego.

Zacommitować, jeśli praca trzyma się kupy. To prawie zawsze właściwa odpowiedź, niedoskonały commit na gałęzi roboczej nic nie kosztuje i poprawisz go później komendami cofania z poprzedniego rozdziału.

Odłożyć na bok, jeśli praca jest w połowie zrobiona:

bash
git stash          # odkłada zmiany na bok
git switch main    # …robisz to, co masz do zrobienia…
git switch -
git stash pop      # przywraca zmiany
Sortie réelle · git stash
Saved working directory and index state WIP on main: 6d495d9 Page d accueil

Wyrzucić, przez git restore ., jeśli zmiany były nic niewarte. Bez możliwości powrotu.

Scalanie gałęzi

Kiedy funkcjonalność jest gotowa, przejdź na gałąź docelową i scal:

bash
git switch main
git merge feature/panier

Kiedy obie gałęzie zmieniły różne pliki, Git scala sam i o nic nie pyta. Ciekawy jest ten drugi przypadek.

Rozwiązanie konfliktu

Oto sytuacja odtworzona na potrzeby tego rozdziału. Na feature/panier druga linia pliku index.html została zastąpiona komunikatem o pustym koszyku. Na main pierwsza linia została zmieniona, żeby dodać nazwę marki. Obie gałęzie ruszyły ten sam plik.

Sortie réelle · git merge feature/panier
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Nic się nie zepsuło. Git scalił, co mógł, a resztę zostawia tobie do rozstrzygnięcia. Pierwsza rzecz do zrobienia: sprawdzić, w którym miejscu jesteś:

bash
git status
Sortie réelle · git status pendant un conflit
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

Żeby dostać samą listę plików do obsłużenia:

bash
git diff --name-only --diff-filter=U

Otwórz plik. Git wstawił w nim znaczniki:

index.html pendant le conflit
<<<<<<< HEAD
<h1>Boutique Gekkode</h1>
<p>Bienvenue sur la boutique.</p>
=======
<h1>Boutique</h1>
<p>Votre panier est vide.</p>
>>>>>>> feature/panier

Czyta się to mechanicznie. Między <<<<<<< HEAD a ======= znajduje się wersja z gałęzi, na której jesteś, tutaj main. Między ======= a >>>>>>> znajduje się wersja z gałęzi, którą scalasz.

Do ciebie należy napisanie poprawnego wyniku. Niekoniecznie jest nim jedna albo druga wersja: tutaj obie zmiany są dobre i muszą współistnieć.

index.html après résolution
<h1>Boutique Gekkode</h1>
<p>Votre panier est vide.</p>

Usuń wszystkie trzy znaczniki, nie mogą zostać w pliku. Potem daj Gitowi znać, że sprawa jest załatwiona, i zakończ scalanie:

bash
git add index.html
git commit -m "Fusionne feature/panier dans main"
Sortie réelle · git log --oneline --graph après la fusion
*   0899b0c Fusionne feature/panier dans main
|  
| * aacf4c0 Panier : message quand le panier est vide
* | 11ff16b Ajoute le nom de la marque au titre
|/  
* 6d495d9 Page d accueil

Commit scalenia ma dwóch rodziców, co widać na grafie.

Anulować wszystko i zacząć od zera

Jeśli konflikt cię przerasta, nic nie musisz:

bash
git merge --abort

Repozytorium wraca dokładnie do stanu sprzed komendy merge. Nic nie ginie, możesz zacząć od nowa później albo omówić sprawę z autorem drugiej gałęzi.

merge czy rebase

Obie komendy włączają pracę z jednej gałęzi do drugiej, ale nie w ten sam sposób.

git merge tworzy commit scalenia, który łączy obie historie. Historia zachowuje dokładny ślad tego, co się wydarzyło, łącznie z tym, że dwie gałęzie istniały równolegle. Graf jest wierny, ale się rozgałęzia.

git rebase odtwarza twoje commity na wierzchu gałęzi docelowej, tak jakbyś pracował od jej ostatniej wersji. Historia zostaje linią prostą, łatwiejszą do czytania, ale zostaje przepisana: twoje commity zmieniają identyfikator.

bash
git switch feature/panier
git rebase main

Rebase może napotkać te same konflikty co scalanie, commit po commicie. Rozwiązuje się je tak samo, różni się tylko komenda pozwalająca kontynuować:

Sortie réelle · conflit pendant un rebase
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply 4937109... Promo
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Could not apply 4937109... Promo
bash
git add index.html
git rebase --continue
# albo, żeby wszystko anulować
git rebase --abort

Złota zasada mieści się w jednym zdaniu: nigdy nie rób rebase gałęzi, którą inni już pobrali. Przepisanie współdzielonej historii zmusza kolegów z zespołu do naprawiania własnej ręcznie. Na twojej własnej gałęzi roboczej, zanim ją zaproponujesz, rebase jest natomiast całkowicie na miejscu i daje dużo czytelniejszą historię.

Codzienny przypadek: ktoś wypchnął zmiany przed tobą

Chcesz wypchnąć zmiany, a Git odmawia:

Sortie réelle · git push refusé
To /root/o.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '/root/o.git'
hint: Updates were rejected because the remote contains work that you do not

Serwer ma commity, których ty nie masz. Musisz je najpierw włączyć. I tu, na świeżym repozytorium, Git zamiast działać zadaje ci pytanie:

Sortie réelle · git pull sans stratégie configurée
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:

Od wersji 2.27 Git nie wybiera za ciebie. Odpowiedz raz na zawsze, tak jak proponuje rozdział o instalacji:

bash
git config --global pull.rebase true

Przebieg wygląda wtedy tak:

bash
git pull --rebase
git push
Sortie réelle · git pull --rebase puis git log
From /root/o
   622a60b..38b7dc5  main       -> origin/main
Rebasing (1/1)Successfully rebased and updated refs/heads/main.

* 5b45117 Ajoute app.js
* 38b7dc5 Ajoute la doc
* 622a60b Depart

Twój commit został przeniesiony nad commit kolegi, a historia została liniowa, bez pasożytniczego commita scalenia. To ustawienie warto polecić zespołowi, który zaczyna.

Na koniec ostrzeżenie: nigdy nie próbuj odblokować odrzuconego push komendą git push --force. Nadpisuje ona pracę obecną na serwerze. Jeśli naprawdę musisz wymusić wypchnięcie, po rebase własnej gałęzi, użyj git push --force-with-lease, które odmawia nadpisania commitów, których nie widziałeś.

Sprzątanie

Scalona gałąź nie ma już racji bytu. Jeśli została opublikowana z -u, najpierw wypchnij jej ostatnie commity: Git odmawia usunięcia lokalnej gałęzi wyprzedzającej swoją gałąź zdalną, nawet gdy jest scalona z main.

bash
git push origin feature/panier
git branch -d feature/panier
Sortie réelle · suppression d'une branche fusionnée
Deleted branch feature/panier (was bc2203a).

Bez tego wcześniejszego push komunikat jest jednoznaczny:

Sortie réelle · branche locale en avance sur origin
warning: not deleting branch 'feature/panier' that is not yet merged to
         'refs/remotes/origin/feature/panier', even though it is merged to HEAD
error: the branch 'feature/panier' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/panier'
hint: Disable this message with "git config set advice.forceDeleteBranch false"

Jeśli gałąź nie została scalona, Git cię zatrzymuje, co jest przydatną siatką bezpieczeństwa:

Sortie réelle · suppression d'une branche non fusionnée
error: the branch 'feature/promo' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/promo'
hint: Disable this message with "git config set advice.forceDeleteBranch false"

Żeby usunąć gałąź także na serwerze i wyczyścić lokalne referencje, które stały się nieaktualne:

bash
git push origin --delete feature/panier
git fetch --prune

git fetch pobiera stan serwera, nie scalając niczego z twoimi gałęziami, w przeciwieństwie do git pull. Opcja --prune usuwa przy okazji te gałęzie zdalne, które już nie istnieją: te, które koledzy z zespołu scalili i skasowali.

Umiesz już współpracować. Ostatni rozdział dotyczy organizacji: jaki model gałęzi wybrać w zależności od zespołu.

Częste błędy

Your local changes would be overwritten by checkout Zacommituj zmiany albo odłóż je na bok przez git stash, zanim zmienisz gałąź.
You have divergent branches Od Gita 2.27 git pull nie wybiera sam między scaleniem a rebase. Rozstrzygnij to przez git config --global pull.rebase true.
! [rejected] non-fast-forward Serwer ma commity, których ty nie masz. Włącz je przez git pull --rebase. Nigdy nie obchodź tego przez git push --force: nadpisuje pracę innych.
Zapomniane znaczniki konfliktu Linie zaczynające się od <<<<<<<, ======= i >>>>>>> muszą zniknąć z pliku przed git add.
Rebase współdzielonej gałęzi Przepisanie historii, którą inni już pobrali, zmusza ich do naprawiania własnej. Rebase rób tylko na swoich niepublikowanych gałęziach.
Newsletter

Nowe testy, poradniki i projekty, e-mailem.

Powtarzalne testy, wersjonowany kod, datowane wyniki. Nigdy spamu.