Skip to content

11. Sincronizzare modifiche locali e remote

11.1 git fetch — scaricare senza applicare

Section titled “11.1 git fetch — scaricare senza applicare”

Scarica commit, branch e tag dal remote nel repository locale, senza applicarli alla working copy: operazione sicura, che non tocca il lavoro in corso.

Terminal window
$ git fetch
c7ed69f..45e66a4 main -> origin/main
* [new branch] some-feature -> origin/some-feature
$ git status
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.

Esempio di situazione iniziale: si è clonato ieri un repository con il solo branch main (3 commit); la working copy ha estratto l’ultimo commit e il branch locale è collegato alla sua controparte remota:

Terminal window
$ git branch -vv
* main c7ed69f [origin/main] implemented first feature

Nel frattempo un collaboratore ha aggiunto commit su main e creato un nuovo branch: dopo il fetch, i nuovi contenuti sono disponibili ma non applicati, perché Git mantiene separati i commit dei branch locali da quelli remoti (origin/main).

Terminal window
git log --oneline main..origin/main # cosa c'è di nuovo nel remote
git merge origin/main # applica i nuovi commit al branch locale
# Fast-forward

git pull = git fetch + integrazione dei contenuti scaricati nella working copy. Sui branch divergenti (commit locali e remoti entrambi nuovi) esistono due strategie.

Scenario d’esempio: si è clonato un repository quando l’ultimo commit su main era il commit “B”. In locale si sono salvati i commit C, D, E; nel frattempo un altro sviluppatore ha inviato al remote i commit F, G, H. Il remote ospita la history “ufficiale”: i nuovi commit vanno recuperati e integrati con i propri, scegliendo la strategia.

Pull con rebase (git pull --rebase — tratta il remote come storia ufficiale):

  1. i commit locali (C-D-E) vengono messi da parte;
  2. fetch dei nuovi commit remoti (F-G-H);
  3. il branch locale e la working copy vengono aggiornati al remote;
  4. i commit locali vengono riapplicati come nuovi commit C’-D’-E’ (stesse differenze, nuovi snapshot).
Terminal window
git config pull.rebase true # imposta come strategia predefinita

Pull con merge (git pull --no-rebase — dà “precedenza” al locale): dopo il fetch viene creato un dedicato merge commit (chiamiamolo I) che unisce le due history; Git chiede di salvarne il messaggio. Il risultato funziona e i branch non sono più divergenti, ma la history risulta meno lineare e la cronologia apparente meno coerente:

$ git log --graph --oneline
* ece860f (HEAD -> main, origin/main) I - Merge branch 'main' of https://server.com/repository.git
|\
| * 09817f7 H - remote commit three
| * 1e0e1d2 G - remote commit two
| * b7d7502 F - remote commit one
* | 4d49b1c E - local commit 3
* | 6453336 D - local commit 2
* | 24540dd C - local commit 1
|/
* 9cc4eee B - another commit
* f46646f A - initial commit

Dal punto di vista cronologico del remote, l’ordine è risultato “invertito”: i commit F-G-H (già esistenti prima dei push di C-D-E) appaiono ora nel grafico dopo i commit locali.

Terminal window
git config pull.rebase false # imposta merge come strategia predefinita

Consiglio: quando si collabora su un branch, è preferibile la modalità con rebase, che mantiene la history più comprensibile.

Nelle versioni recenti di Git, un pull su branch divergenti senza strategia indicata viene interrotto proprio per costringere a scegliere.

Carica i commit locali sul remote (solo i nuovi: Git sa già cosa c’è nel remote).

Terminal window
git push # invia il branch corrente al suo remote collegato
git push <remote> <branch> # invia un branch specifico a un remote specifico
git push --tags # invia i tag (non vengono inviati automaticamente!)
git push --all # invia tutti i branch locali

Push forzato dopo aver riscritto la history: se si fa un amend (o un rebase) di commit già inviati, locale e remote divergono e il push normale viene rifiutato. Serve --force:

Terminal window
$ git commit --amend --no-edit
$ git push --force
+ 7b82870...5fb65cb main -> main (forced update)

⚠️ --force sovrascrive interamente la history del branch remoto: se nel frattempo altri hanno inviato commit, andranno persi. Buone pratiche:

  • fare sempre fetch/pull prima di un push forzato;
  • usare --force-with-lease invece di --force: avvisa se si stanno per rimuovere commit altrui, permettendo di recuperarli prima di riprovare.