DanLevy.net

Deathmatch: Git Rebase vs. Merge

Una domanda senza tempo...

Hero image for Deathmatch: Git Rebase vs. Merge

Deathmatch: Git Rebase vs. (Squash) Merge!

Dovrei fare il rebase? O lo squash merge?

Perché questo argomento evoca un fervore religioso?

Alcuni ingegneri usano la conoscenza di git (e del terminale) come segnale del loro livello di abilità relativo. E qualsiasi pratica legata alla nostra identità/ego può essere impossibile da analizzare con imparzialità, figuriamoci da cambiare.

Altri fattori probabilmente includono la familiarità e il bias del sopravvissuto, che possono ulteriormente offuscare la nostra valutazione e le nostre ipotesi.

Domanda chiave: qual è lo scopo di un commit git?

  1. Fai commit presto e spesso? Con una mentalità da “checkpoint” o backup?
    • Dove tutto viene registrato, anche i falsi inizi e gli esperimenti? (es. git commit -am "Updated deps" && git push, da ripetere regolarmente)
    • Forse i messaggi di commit sono meno importanti del codice per te?
  2. Oppure i tuoi commit sono un’opera d’arte accuratamente curata e scolpita?
    • Forse ogni commit è un’unità di lavoro atomica e autosufficiente? (es. git add package.json && git commit -m "Updated deps")
    • O semplicemente non sopporti i log di commit “disordinati”?
    • Le tue revisioni PR coinvolgono spesso una revisione commit per commit?

| 💡 Quali altri modelli mentali definiscono il modo in cui vedi i commit? Fatemelo sapere @justsml!

Stai pensando a git in un modo che sta fornendo il massimo valore a te, al tuo team e alla tua organizzazione?

Dato che esistono mentalità molto diverse riguardo alla strategia di commit, non c’è da stupirsi se c’è così tanta confusione sul modo “giusto” di usare git.

Scenario: Creare un tag di rilascio revisionato

Confrontiamo il processo di creazione di un tag di rilascio escludendo alcuni commit recenti su main.

Rilascio con Git Tag da main con 2 branch di funzionalità

Il modo Rebase

Modello mentale: “Voglio creare una versione alternativa di una cronologia esistente. (es. Ho fatto un errore 16 merge fa, e potrei aver bisogno di un controllo granulare per correggerlo. Inoltre, potrei rimanere bloccato in un ciclo apparentemente infinito di conflitti e --continue.)”

  1. Ottieni gli ultimi aggiornamenti: git checkout main && git pull
  2. Crea un nuovo branch: git checkout -b release/hot-newness-and-stuff
  3. Avvia il rebase interattivo e includi il riferimento git per il punto nel tempo a cui vuoi tornare. git rebase -i HEAD~6 (Nota: HEAD~6 è una scorciatoia per ‘6 commit fa’)
  4. Scarta i commit desiderati cambiando il loro prefisso in drop. Salva e chiudi l’editor.
  5. Risolvi i conflitti di merge, git add . && git rebase --continue (NON fare git commit).
  6. Ripeti il passaggio precedente fino al completamento.
  7. Crea il tag/push usando il processo corrente. Esempio git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Pro

Contro

Il modo (Squash) Merge

Modello mentale: “Voglio un rilascio personalizzato, a partire da un dato punto, e includendo i branch desiderati.”

  1. Ottieni gli ultimi aggiornamenti: git checkout main && git pull
  2. Crea un nuovo branch: git checkout -b release/hot-newness-and-stuff
  3. Merge dei branch e/o commit desiderati: git merge --no-ff feature/hot-newness bug/fix-123 (usa il flag --no-ff dove possibile.)
  4. Risolvi eventuali conflitti di merge (dovessero presentarsi.)
  5. Crea il tag/push usando il processo corrente. Esempio git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Pro

Contro

Conclusione

Alla fine, un processo più semplice con meno rischi dovrebbe prevalere.

Anche se i Rebaser hanno certamente modi per risolvere (o evitare) i loro problemi, il fatto resta: avrai bisogno di una cintura nera nel kung fu di git. (es. Anche un umile git push può affrontare una complessità extra: era git push --force o git push --force-with-lease? Perché occuparsene affatto?)

C’è un altro motivo per cui il rebasing per creare una cronologia revisionata sarà sempre in svantaggio rispetto a git merge .... Un git merge permette alla CLI git di applicare algoritmi avanzati per evitare conflitti analizzando l’HEAD di ogni branch.

Questo può essere più intelligente perché ogni merge si preoccupa solo dello stato più recente di ogni branch desiderato, mentre il rebasing deve ri-eseguire (o scartare) la cronologia dei commit nella sequenza specificata. Questo limita la capacità di git di ottimizzare il merge poiché confronta solo 2 commit alla volta.

Alla fine, il rebasing significa che occasionalmente ti ritroverai a rivivere commit e conflitti obsoleti irrilevanti - anche se sai che sono stati rimossi o risolti da tempo.