La differenza tra merge e rebase in Git

Né il rebase né il merge durante un pull cancellano autonomamente i tuoi lavori — in entrambi i casi i tuoi commit rimangono nel repository (nella storia della branch o come vecchi SHAs finché il reflog è attivo).

  • pull.rebase false (merge): git pull creerà un commit di merge, i tuoi e gli altri commit non vengono sovrascritti, la storia della branch si unisce semplicemente. È più “sicuro per la mente”: vedi chiaramente le linee che si incontrano.
  • pull.rebase true (rebase): i tuoi commit locali vengono copia su quelli remoti (nuovi hash). Il contenuto dei commit è generalmente lo stesso, ma i “vecchi” commit dopo un rebase riuscito possono rimanere solo nel git reflog — è normale, da lì puoi ripristinarli (git reset --hard al ref desiderato dal reflog) se qualcosa va storto.

Se l’obiettivo principale è “non perdere i lavori”:

  1. Prima di operazioni rischiose — commit (o git stash) e, se vuoi, una branch separata (git branch backup-tgisn-46) — una copia dell’indicatore, un backup economico.
  2. Non confondere con perdite causate da comandi come git reset --hard, force push senza necessità, git clean -fd.
  3. Dopo un rebase/merge, non eseguire git gc in modo aggressivo nei primi giorni — il reflog conserva un punto di ritorno.

Pratico per “non perdere, non complicare troppo la storia”: git config pull.rebase false — in caso di conflitto, otterrai sempre un merge, senza sovrascrivere i commit locali. Se hai già deciso di preferire il rebase (come nel vecchio pull --rebase) — puoi lasciare pull.rebase true, semplicemente non push --force su branch condivise senza accordo.

Riepilogo: non si perdono i lavori a causa della scelta della strategia pull; il rischio è nelle altre comandi e nei force push; per tranquillità — commit + branch di backup prima di operazioni complesse.

Ecco la stessa situazione che hai descritto: un singolo commit comune C, poi su origin cinque commit r1…r5, sulla local — uno solo L (il mio lavoro).

1. Prima del pull (divergenza)

gitGraph
  commit id: "C (comune)"
  branch local
  checkout local
  commit id: "L (mio commit)"
  checkout main
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"

Significato: la punta di local è L, la punta di main è r5, il comune antenato è C. Questo non è perso in entrambi i casi — cambia solo come la storia viene unita.


2. Dopo git pull senza rebase (pull.rebase falsemerge)

gitGraph
  commit id: "C"
  branch local
  checkout local
  commit id: "L"
  checkout main
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"
  checkout local
  merge main
  commit id: "M (commit di merge)"

Si crea un commit di merge M con due genitori (L e r5). I commit L e r1…r5 come oggetti rimangono; la storia è ramificata, senza sovrascritture.


3. Dopo git pull con rebase (pull.rebase truerebase)

gitGraph
  commit id: "C"
  commit id: "r1"
  commit id: "r2"
  commit id: "r3"
  commit id: "r4"
  commit id: "r5"
  commit id: "L′ (stesso patch, nuovo hash)"

La storia è lineare: il tuo lavoro si trova sopra quelli remoti. Il tuo commit L nella storia diventa un nuovo commit L′ (stesso diff rispetto a r5, hash diverso); il vecchio L rimane visibile nel git reflog per un po’ finché non rimuovi l’impostazione reflog expire.


In breve: merge — ramificazione + nodo di merge; rebase — una linea, il tuo commit “riprodotto” sopra. I lavori, nel senso delle modifiche nei file, non vanno persi in entrambi i casi normali; cambia solo la forma della storia e gli hash del commit locale nel caso di rebase.