Capitolo 4 di 5

Tutorial Git: lavorare in team con i branch e il merge

verificato il 7 Settembre 2026 · 9 min

Risposta rapida

Crea un branch con git switch -c nom, lavora, poi fondilo con git merge dal branch di destinazione. In caso di conflitto Git inserisce dei marcatori nel file: scrivi il contenuto giusto, elimina i marcatori, poi git add e git commit. git merge --abort annulla tutto in qualsiasi momento.

Un branch è una linea di sviluppo parallela. Ti permette di lavorare su una funzionalità senza toccare la versione stabile, e al tuo collega di fare lo stesso dalla sua parte. Questo capitolo copre il ciclo completo: creare un branch, fonderlo, risolvere un conflitto a mano e scegliere tra merge e rebase.

git switch al posto di git checkout

Storicamente git checkout serviva a tutto: cambiare branch, crearne uno, ripristinare un file, posizionarsi su un commit preciso. Questo accumulo di ruoli è la prima fonte di confusione per chi inizia, e una fonte di incidenti per tutti gli altri.

Git 2.23, uscito ad agosto 2019, ha diviso il comando in due: git switch per spostarsi tra i branch, git restore per annullare le modifiche. All’uscita erano marcati come sperimentali, nella documentazione ufficiale non lo sono più. Questo capitolo li usa e indica sempre l’equivalente con checkout, che continuerai a incontrare a lungo nei tutorial e su Stack Overflow.

Intenzione Comando moderno Forma precedente
Cambiare branch git switch nom git checkout nom
Creare un branch e spostarcisi git switch -c nom git checkout -b nom
Tornare al branch precedente git switch - git checkout -
Annullare la modifica di un file git restore fichier git checkout -- fichier
Togliere un file dall’indice git restore --staged fichier git reset HEAD fichier

Creare un branch e lavorarci

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

Il branch parte dal commit su cui ti trovavi. Ora puoi modificare, aggiungere e committare normalmente: niente di tutto questo comparirà su main finché non lo avrai fuso.

Sui nomi dei branch Git non impone nessuna regola, ma i prefissi feature/, fix/ e hotfix/ sono una convenzione diffusa e le interfacce delle piattaforme di hosting li raggruppano visivamente.

Per vedere a che punto sei:

bash
git branch              # i branch locali
git branch -vv          # con l'ultimo commit e il branch remoto tracciato
git branch -a           # locali e remoti
git switch -            # tornare al branch precedente

Per pubblicare il branch e renderlo visibile al team:

bash
git push -u origin feature/panier

Recuperare il branch di un collega

Nel senso opposto, git fetch aggiorna quello che sai del server senza modificare nulla nei tuoi branch:

bash
git fetch origin feature/collegue   # un branch specifico
git fetch --all                     # tutti i repository remoti
git branch -r                       # quello che sai del server
Sortie réelle · git branch -r après un fetch
  origin/HEAD -> origin/main
  origin/feature/collegue
  origin/main

Non resta che spostarti su di esso. Git crea automaticamente il branch locale e lo collega a quello del server:

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'

Quando Git rifiuta di cambiare branch

È uno dei primi errori che incontrerai:

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 protegge il tuo lavoro: il file che hai modificato non ha lo stesso contenuto sull’altro branch, e cambiare branch lo sovrascriverebbe. Tre soluzioni, in ordine di preferenza.

Committare, se il lavoro è coerente. È quasi sempre la risposta giusta, un commit imperfetto su un branch di lavoro non costa niente e si sistema più tardi con i comandi di annullamento visti nel capitolo precedente.

Mettere da parte, se il lavoro è a metà:

bash
git stash          # mette da parte le modifiche
git switch main    # …fai quello che dovevi fare…
git switch -
git stash pop      # recupera le modifiche
Sortie réelle · git stash
Saved working directory and index state WIP on main: 6d495d9 Page d accueil

Buttare, con git restore ., se le modifiche non valevano niente. Senza ritorno possibile.

Fondere un branch

Una volta finita la funzionalità, spostati sul branch di destinazione e fondi:

bash
git switch main
git merge feature/panier

Quando i due branch hanno modificato file diversi, Git fonde da solo e non ha niente da chiederti. Il caso interessante è l’altro.

Risolvere un conflitto

Ecco la situazione riprodotta per questo capitolo. Su feature/panier la seconda riga di index.html è stata sostituita da un messaggio di carrello vuoto. Su main la prima riga è stata modificata per aggiungere il nome del marchio. I due branch hanno toccato lo stesso file.

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.

Niente è rotto. Git ha fuso quello che poteva e lascia a te il resto. Prima cosa da fare, chiedere a che punto sei:

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")

Per ottenere solo l’elenco dei file da sistemare:

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

Apri il file. Git ci ha inserito dei marcatori:

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

La lettura è meccanica. Tra <<<<<<< HEAD e ======= si trova la versione del branch su cui sei, qui main. Tra ======= e >>>>>>> si trova la versione del branch che stai fondendo.

Tocca a te scrivere il risultato corretto. Non è per forza l’una o l’altra: qui le due modifiche sono entrambe valide e devono convivere.

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

Elimina i tre marcatori, non devono restare nel file. Poi segnala a Git che è risolto e concludi la fusione:

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

Il commit di merge ha due genitori, e il grafo lo rende visibile.

Annullare tutto e ripartire da zero

Se il conflitto ti sfugge di mano, non sei obbligato a niente:

bash
git merge --abort

Il repository torna esattamente allo stato precedente al comando merge. Non si perde niente: puoi ricominciare più tardi o parlarne con l’autore dell’altro branch.

merge o rebase

Entrambi integrano il lavoro di un branch in un altro, ma non allo stesso modo.

git merge crea un commit di merge che collega le due cronologie. La cronologia conserva la traccia esatta di quello che è successo, compreso il fatto che due branch sono esistiti in parallelo. Il grafo è fedele, ma si ramifica.

git rebase riapplica i tuoi commit sopra il branch di destinazione, come se avessi lavorato a partire dalla sua ultima versione. La cronologia resta una linea dritta, più facile da leggere, ma viene riscritta: i tuoi commit cambiano identificativo.

bash
git switch feature/panier
git rebase main

Un rebase può incontrare gli stessi conflitti di una fusione, commit per commit. La risoluzione è identica, cambia solo il comando per proseguire:

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
# oppure, per annullare tutto
git rebase --abort

La regola d’oro sta in una frase: non fare mai il rebase di un branch che altre persone hanno già scaricato. Riscrivere una cronologia condivisa obbliga i tuoi colleghi a riparare la loro a mano. Sul tuo branch di lavoro, prima di proporlo, il rebase è invece perfettamente legittimo e restituisce una cronologia molto più leggibile.

Il caso quotidiano: qualcuno ha pushato prima di te

Vuoi fare push e Git rifiuta:

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

Il server ha commit che tu non hai. Devi prima integrarli. E qui, su un repository nuovo, Git ti fa una domanda invece di agire:

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:

Dalla versione 2.27 Git si rifiuta di scegliere al posto tuo. Rispondi una volta per tutte, come proposto nel capitolo sull’installazione:

bash
git config --global pull.rebase true

Lo svolgimento diventa allora questo:

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

Il tuo commit è stato riposizionato sopra quello del collega e la cronologia è rimasta lineare, senza commit di merge parassita. È l’impostazione da consigliare a un team che inizia.

Un avvertimento per finire: non provare mai a sbloccare un push rifiutato con git push --force. Questo comando sovrascrive il lavoro presente sul server. Se devi davvero forzare, dopo il rebase di un tuo branch, usa git push --force-with-lease, che si rifiuta di sovrascrivere commit che non avevi visto.

Fare pulizia

Un ramo fuso non ha più motivo di esistere. Se è stato pubblicato con -u, spingete prima i suoi ultimi commit: Git si rifiuta di eliminare un ramo locale in anticipo sul suo ramo remoto, anche se fuso in 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).

Senza quel push preliminare, il messaggio è esplicito:

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"

Se il branch non è stato fuso, Git ti ferma, ed è una rete di sicurezza utile:

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"

Per eliminare il branch anche sul server e ripulire i riferimenti locali ormai obsoleti:

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

git fetch recupera lo stato del server senza fondere niente nei tuoi branch, a differenza di git pull. L’opzione --prune elimina di passaggio i branch remoti che non esistono più, quelli che i tuoi colleghi hanno fuso e cancellato.

Ora sai collaborare. L’ultimo capitolo parla di organizzazione: quale modello di branch adottare in base al tuo team.

Errori frequenti

Your local changes would be overwritten by checkout Fai il commit delle tue modifiche, oppure mettile da parte con git stash, prima di cambiare branch.
You have divergent branches Da Git 2.27 git pull si rifiuta di scegliere da solo tra fusione e rebase. Chiudi la questione con git config --global pull.rebase true.
! [rejected] non-fast-forward Il server ha commit che tu non hai. Integrali con git pull --rebase. Non usare mai git push --force per aggirare il problema: sovrascrive il lavoro degli altri.
Marcatori di conflitto dimenticati Le righe che iniziano con <<<<<<<, ======= e >>>>>>> devono sparire dal file prima del git add.
Rebase di un branch condiviso Riscrivere una cronologia che altri hanno già scaricato li obbliga a riparare la loro. Fai il rebase solo dei tuoi branch non pubblicati.
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.