Confronto

git fetch e git pull: qual è la differenza e quale scegliere

Aggiornato: · Skillaria.Top

git fetch scarica solo i nuovi commit dal server e aggiorna i branch remoti come origin/main, senza toccare i vostri file e branch. git pull fa la stessa cosa e integra subito le modifiche nel branch corrente con merge o rebase. Fetch è sempre sicuro, pull è comodo quando siete pronti ad accettare subito le modifiche altrui.

La risposta breve

git fetch scarica dal server i nuovi commit e aggiorna i riferimenti dei branch remoti (origin/main, origin/feature-x), ma non modifica né il vostro branch corrente né i file nella working directory. git pull è git fetch più l'integrazione immediata di quanto scaricato nel branch corrente: con un merge (di default) o con un rebase.

Da qui la regola pratica: fetch si può lanciare in qualsiasi momento, non rompe nulla. Pull modifica il vostro branch e può finire con un conflitto, quindi conviene lanciarlo con la working directory pulita e sapendo cosa sta per arrivare.

Dove vivono i branch remoti

Perché la differenza diventi evidente, bisogna capire che nel repository locale esistono due tipi di branch. Quelli locali, main e feature-x, li fate avanzare voi con i vostri commit. I branch remoti di tracking, come origin/main, sono segnalibri del tipo «così era il server al momento dell'ultima sincronizzazione». Non si possono spostare a mano: li aggiorna solo la comunicazione con il server, cioè fetch, pull e push.

git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
  remotes/origin/feature-x

git status confronta il vostro branch proprio con il segnalibro origin/main, non con il server reale. Per questo il messaggio «Your branch is up to date with 'origin/main'» può mentire: se non fate fetch da tempo, Git semplicemente non sa dei nuovi commit dei colleghi.

Cosa fa git fetch

$ git fetch
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
Unpacking objects: 100% (4/4), done.
From github.com:team/project
   3f2a1c9..8b7d4e2  main       -> origin/main

L'ultima riga dice l'essenziale: il riferimento origin/main si è spostato da 3f2a1c9 a 8b7d4e2. Il vostro main è rimasto dov'era. Ora potete guardare con calma cosa è arrivato:

git status                       # Your branch is behind 'origin/main' by 2 commits
git log main..origin/main --oneline   # i commit che non avete
git diff main origin/main        # cosa cambierà nei file

Varianti utili di fetch:

  • git fetch --all: scarica le modifiche da tutti i remote (utile se un fork ha sia origin sia upstream);
  • git fetch --prune (-p): elimina i segnalibri locali dei branch che non esistono più sul server;
  • git fetch origin main: scarica un solo branch;
  • git fetch --tags: scarica in più tutti i tag.

Perché il prune sia sempre attivo, abilitatelo una volta: git config --global fetch.prune true.

Cosa fa git pull

git pull esegue due passaggi di fila. Prima un fetch per il branch upstream del branch corrente, poi l'integrazione di origin/<branch> nel vostro branch. Letteralmente equivale a:

git fetch origin
git merge origin/main      # oppure git rebase origin/main con pull --rebase

Il risultato dell'integrazione dipende da come si sono separate le storie:

  • non avete nuovi commit: avviene un fast-forward, il branch viene semplicemente portato avanti;
  • ci sono nuovi commit sia da voi sia sul server: con merge si crea un commit di merge, con rebase i vostri commit vengono riposizionati sopra quelli del server;
  • le stesse righe sono state modificate da entrambe le parti: conflitto, da risolvere a mano.

Confronto tra git fetch e git pull

git fetchgit pull
Scarica i commitSìSì
Aggiorna origin/*SìSì
Modifica il branch correnteNoSì
Modifica i file nella working directoryNoSì
Può causare un conflittoNoSì
Richiede una working directory pulitaNoPreferibilmente
Coinvolge altri branchAggiorna tutti i segnalibri tracciatiIntegra solo nel corrente
Quando usarloVedere cosa è cambiato; prima di un rebase; negli scriptMettersi rapidamente in pari con il server nel proprio branch

git pull: merge o rebase

Da Git 2.27, quando le storie divergono e non c'è una configurazione esplicita, pull mostra un avviso, e nelle versioni recenti invece di un merge silenzioso si ferma con l'errore «Need to specify how to reconcile divergent branches». Git vi chiede di scegliere una strategia una volta per tutte:

git config --global pull.rebase false   # merge (il comportamento predefinito di un tempo)
git config --global pull.rebase true    # rebase
git config --global pull.ff only        # solo fast-forward, altrimenti rifiuto

Merge conserva la storia così com'è e aggiunge un commit «Merge branch 'main' of github.com:...». Se fate pull ogni giorno in un branch con commit locali, la storia si riempie di questi merge tecnici.

Rebase riposiziona i vostri commit non inviati sopra quelli freschi del server. La storia resta lineare e i conflitti si risolvono un commit alla volta. Per i commit locali non pubblicati è di solito la scelta migliore. Rebase è pericoloso solo per commit già inviati, su cui altri stanno costruendo il proprio lavoro.

ff only è la modalità più rigida: pull funziona solo se non avete commit vostri. Se la storia è divergente, decidete esplicitamente cosa fare. Molti sviluppatori esperti la impostano proprio per i branch principali.

Se nella working directory ci sono modifiche non committate e il pull serve subito, torna utile git pull --rebase --autostash: Git mette da parte le modifiche, esegue il rebase e le rimette al loro posto.

Lo scenario tipico: fetch, guardare, integrare

L'approccio «prima fetch» dà il controllo su ciò che entra nel vostro branch. È particolarmente utile prima di aprire una pull request e dopo una lunga pausa.

git fetch
git log --oneline --graph HEAD origin/main   # come si sono separati i branch
git rebase origin/main                       # oppure git merge origin/main
git push

Un altro caso frequente è aggiornare il proprio branch feature con il main recente senza passare su di esso:

git switch feature-x
git fetch
git rebase origin/main

Qui pull non va bene: git pull nel branch feature scaricherebbe le modifiche del suo upstream (origin/feature-x), non del branch principale. Si potrebbe scrivere git pull --rebase origin main, ma la variante con fetch è più chiara.

Sincronizzazione con il server: come tenere il branch aggiornato

Chi cerca «git sync» di solito intende il ciclo «prendere il lavoro altrui, inviare il proprio». Nel Git puro non esiste un comando sync (il pulsante Sync c'è negli IDE e nei client grafici, e internamente fa pull e poi push). A mano, un ciclo affidabile è questo:

  1. git status: verificare che tutto sia committato o messo da parte con git stash;
  2. git pull --rebase: scaricare le modifiche e riposizionare i propri commit in cima;
  3. risolvere eventuali conflitti: correggere i file, git add, git rebase --continue;
  4. lanciare i test;
  5. git push.

Per sincronizzare un fork con il repository originale:

git remote add upstream https://github.com/original/project.git
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main

Problemi frequenti con git pull

Your local changes would be overwritten by merge

Pull vuole modificare file che state modificando e non avete committato. Opzioni: fare commit, mettere da parte (git stash, poi pull e git stash pop) o usare --autostash. Non serve cancellare le modifiche per fare pull.

Conflitto dopo il pull

Git si ferma e mostra i file in conflitto in git status. Apriteli, scegliete la variante giusta tra i marcatori <<<<<<< e >>>>>>>, poi git add e git commit (con merge) o git rebase --continue (con rebase). Ci avete ripensato? git merge --abort o git rebase --abort riportano tutto com'era prima del pull.

There is no tracking information for the current branch

Il branch non ha un upstream configurato e Git non sa da dove scaricare. Indicatelo esplicitamente: git pull origin main, oppure configurate il collegamento una volta: git branch --set-upstream-to=origin/main.

refusing to merge unrelated histories

Il repository locale e quello remoto sono stati creati in modo indipendente, ad esempio sul server è stato aggiunto un README alla creazione. Se siete sicuri di volerli unire: git pull origin main --allow-unrelated-histories.

Un pull andato male: come tornare indietro

Se il merge dopo il pull è già concluso e il risultato non vi soddisfa, riportate il branch allo stato precedente: git reset --hard ORIG_HEAD. Il comando elimina le modifiche non committate, quindi prima controllate git status. Nel peggiore dei casi la posizione precedente del branch si trova in git reflog.

Cosa scegliere

  • Volete sapere cosa c'è di nuovo senza cambiare nulla: git fetch.
  • Lavorate nel vostro branch e vi mettete semplicemente in pari con il server: git pull --rebase (o pull con pull.rebase true configurato).
  • Aggiornate il branch principale senza commit vostri: git pull --ff-only.
  • Dovete integrare main in un branch feature: git fetch e poi git rebase origin/main o git merge origin/main.
  • Scrivete uno script o una CI: fetch e comandi espliciti, senza pull.

Come appare sul grafo dei commit

Immaginate che voi e un collega siate partiti dal commit C. Voi avete fatto in locale il commit D, e il collega ha inviato al server E. Dopo git fetch il vostro repository conosce entrambe le linee: main punta a D, origin/main a E, e hanno l'antenato comune C.

          D   <- main (HEAD)
         /
A---B---C
         \
          E   <- origin/main

Da qui decide tutto il metodo di integrazione. git merge origin/main crea un commit M con due genitori, D ed E. git rebase origin/main copia D sopra E come nuovo commit D' con un hash diverso, e la storia diventa una linea retta. git pull esegue semplicemente questi stessi passaggi in automatico, senza pause tra l'uno e l'altro. Proprio quella pausa, la possibilità di guardare il grafo prima dell'integrazione, è il valore principale di un fetch separato.

Fetch e pull negli IDE

In VS Code, IntelliJ IDEA e altri editor i pulsanti hanno nomi diversi, ma dietro ci sono gli stessi comandi. «Fetch» è git fetch, «Pull» o «Update Project» è un pull con la strategia scelta nelle impostazioni (merge o rebase), «Sync Changes» è pull seguito da push. Prima di premere Sync su un branch condiviso conviene sapere quale strategia è impostata, altrimenti nella storia compariranno commit di merge inattesi. Molti IDE fanno anche fetch periodici in background, quindi il contatore dei commit in arrivo nella barra di stato di solito è aggiornato.

In sintesi

git fetch aggiorna ciò che sapete del server, git pull applica anche questa conoscenza al branch corrente. Fetch è sicuro in qualsiasi momento, pull è comodo ma modifica il branch e può portare un conflitto, quindi configuratene la strategia una volta e tenete pulita la working directory. La differenza tra push, pull e fetch, il lavoro con i remote e con GitHub sono trattati in pratica nel corso su Git.

Domande frequenti

Bisogna fare git fetch prima di git pull?

No, pull esegue da solo il fetch come primo passaggio. Un fetch separato serve quando volete prima guardare le modifiche e poi decidere come integrarle.

git fetch può cancellare le mie modifiche?

No. Fetch modifica solo i riferimenti remoti come origin/main e non tocca i branch locali, l'indice e la working directory.

git pull aggiorna tutti i branch?

No, pull integra le modifiche solo nel branch corrente. I segnalibri origin/* degli altri branch vengono aggiornati grazie al fetch, ma i branch locali restano dove sono.

Ogni quanto fare git fetch?

Prima di iniziare a lavorare, prima di creare una pull request e prima del push. Molti IDE fanno fetch automaticamente in background, ed è sicuro.

Cos'è FETCH_HEAD?

Un riferimento di servizio in cui fetch registra l'ultima cosa scaricata. Ad esempio, dopo git fetch origin main si può eseguire git merge FETCH_HEAD.

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».

Vai al corso