Perché git pull esegue per impostazione predefinita un'unione anziché un rebase?


71

Considera la seguente situazione:

  • Hai un clone di un repository git
  • Hai alcuni commit locali (commit che non sono stati ancora spinti da nessuna parte)
  • Il repository remoto ha nuovi commit che non hai ancora riconciliato

Quindi qualcosa del genere:

Impegni non uniti

Se esegui git pullle impostazioni predefinite, otterrai qualcosa del genere:

unisci commit

Questo perché git ha eseguito un'unione.

C'è un'alternativa, però. Puoi dire a pull di fare un rebase invece:

git pull --rebase

e otterrai questo:

ricalcolato

A mio avviso, la versione ridisegnata presenta numerosi vantaggi che si concentrano principalmente sulla pulizia del codice e della cronologia, quindi sono un po 'colpito dal fatto che git si fonda per impostazione predefinita. Sì, gli hash dei tuoi commit locali verranno cambiati, ma questo sembra un piccolo prezzo da pagare per la cronologia più semplice che ottieni in cambio.

Non sto affatto suggerendo che questo sia in qualche modo un default cattivo o sbagliato. Ho solo problemi a pensare ai motivi per cui l'unione potrebbe essere preferita per impostazione predefinita. Abbiamo qualche idea sul perché sia ​​stato scelto? Ci sono vantaggi che lo rendono più adatto come predefinito?

La motivazione principale per questa domanda è che la mia azienda sta cercando di stabilire alcuni standard di base (si spera, più come linee guida) per il modo in cui organizziamo e gestiamo i nostri repository per facilitare agli sviluppatori l'approccio a un repository con cui non hanno mai lavorato prima. Sono interessato a sostenere che dovremmo in genere ribattere in questo tipo di situazione (e probabilmente per aver raccomandato agli sviluppatori di impostare la loro configurazione globale su rebase per impostazione predefinita), ma se mi fossi opposto a questo, certamente mi chiederei perché rebase non è ' t il default se è così grande. Quindi mi chiedo se c'è qualcosa che mi manca.

È stato suggerito che questa domanda è un duplicato di Perché molti siti Web preferiscono "git rebase" piuttosto che "git merge"? ; tuttavia, questa domanda è in qualche modo il contrario di questa. Discute i meriti di rebase su merge, mentre questa domanda pone i vantaggi di merge over rebase. Le risposte lì riflettono questo, concentrandosi sui problemi con l'unione e i benefici di rebase.



L'altro problema è che se ti impegni accidentalmente a master, quindi esegui un pull che si fonde, i due master possono essere fuori sincrono anche se l'unione sembra normale. Questo è il motivo per cui il mio alias pull include --ff-only.
Ixrec,

se sourcetree cambia mai la sua vista grafica in modo che mostri rebasi / fusioni in modo diverso, passerai alla fusione?
Ewan,

1
@Ewan SourceTree non dovrebbe cambiare la sua vista grafica di questo. Rappresenta accuratamente il grafico.
jpmc26,

4
Per gli elettori dupe, si ricorda che il contenuto nella risposta ho accettato è sicuramente non è presente nella domanda si sta affermando è un duplicato.
jpmc26,

Risposte:


56

È difficile sapere con certezza perché la fusione è l'impostazione predefinita senza avere notizie dalla persona che ha preso quella decisione.

Ecco una teoria ...

Git non può presumere che sia ok - cancellare ogni tiro. Ascolta come suona. "Rebase ogni tiro." suona male se usi richieste pull o simili. Riformuleresti su una richiesta pull?

In un team che non utilizza solo Git per il controllo centralizzato del codice sorgente ...

  • Puoi tirare da monte e da valle. Alcune persone fanno molto tirando da valle, da collaboratori, ecc.

  • È possibile lavorare su funzionalità in stretta collaborazione con altri sviluppatori, estrarre da essi o da un ramo di argomento condiviso e ancora occasionalmente aggiornato da upstream. Se rifatti sempre, finisci per cambiare la storia condivisa, per non parlare dei divertenti cicli di conflitto.

Git è stato progettato per un grande team altamente distribuito in cui tutti non tirano e spingono verso un singolo repository centrale. Quindi il valore predefinito ha senso.

  • Gli sviluppatori che non sanno quando va bene il rebase si uniranno per impostazione predefinita.
  • Gli sviluppatori possono rifarsi quando vogliono.
  • I committenti che fanno molti pull e hanno molti pull ottengono il valore predefinito più adatto a loro.

Per prove di intenti, ecco un link a una nota e-mail di Linus Torvalds con le sue opinioni su quando non dovrebbero rifarsi. Dri-devel git pull email

Se segui l'intero thread, puoi vedere che uno sviluppatore sta tirando da un altro sviluppatore e Linus sta tirando da entrambi. Rende piuttosto chiara la sua opinione. Dato che probabilmente ha deciso le impostazioni predefinite di Git, questo potrebbe spiegare il perché.

Molte persone ora usano Git in modo centralizzato, dove tutti in una piccola squadra si spostano solo da un repository centrale a monte e spingono verso lo stesso telecomando. Questo scenario evita alcune delle situazioni in cui un rebase non è buono, ma di solito non le elimina.

Suggerimento: non formulare una politica di modifica del valore predefinito. Ogni volta che metti insieme Git con un folto gruppo di sviluppatori, alcuni sviluppatori non capiranno Git così profondamente (me compreso). Andranno su Google, SO, riceveranno consigli sul ricettario e poi si chiederanno perché alcune cose non funzionano, ad es. Perché git checkout --ours <path>ottiene la versione sbagliata del file? Puoi sempre rivedere il tuo ambiente locale, creare alias, ecc. Secondo i tuoi gusti.


5
+1 per non modificare il valore predefinito - piuttosto che rotolare il proprio flusso di lavoro git è probabilmente meglio afferrarne uno esistente (ad esempio uno di quelli documentati da Atlassian atlassian.com/git/tutorials/comparing-workflows/… )
Rob Church

1
Grazie per la risposta. Le informazioni su come tirare da valle erano ciò che mi mancava. Inoltre, abbastanza interessante, il link che hai dato incoraggia esattamente ciò che vorrei ottenere: "Le persone possono (e probabilmente dovrebbero) cambiare il proprio albero privato (il proprio lavoro)".
jpmc26,

2
@jpmc Ottimo. Infatti. Il rifacimento di alberi privati ​​può impedire a molti greggi di distrarre dalla storia condivisa. Lo faccio. È la chiarezza su quando non farlo è importante.
joshp,

"alcuni sviluppatori non capiranno Git così profondamente". Rebase NON è una funzionalità super fantasiosa ("profonda", ecc.) Di Git. Consideri davvero questa cosa così difficile da capire? Penso che questo dovrebbe essere un fattore scatenante per definire lo sviluppo incompetente.
Victor Yarema,

15

Se leggi la manpage git per rebase, dice :

Rebasing (o qualsiasi altra forma di riscrittura) su un ramo su cui altri hanno basato il lavoro è una cattiva idea: chiunque a valle di esso è costretto a riparare manualmente la propria cronologia. Questa sezione spiega come eseguire la correzione dal punto di vista del downstream. La vera soluzione, tuttavia, sarebbe quella di evitare di reinserire l'upstream in primo luogo.

Penso che lo dica abbastanza bene come motivo per non usare affatto rebase, figuriamoci farlo automaticamente per ogni pull. Alcune persone considerano il rebase dannoso . Forse non avrebbe mai dovuto essere messo a tacere, dato che tutto ciò che sembra fare è prettificare la storia, qualcosa che non dovrebbe essere necessario in nessun SCM il cui unico lavoro essenziale è preservare la storia.

Quando dici "mantenere ... la storia pulita", pensi di sbagliarti. Può sembrare più bello, ma per uno strumento progettato per mantenere la cronologia delle revisioni, è molto più pulito mantenere ogni impegno in modo da poter vedere cosa è successo. Disinfettare la storia in seguito è come lucidare la patina, facendo sembrare un ricco oggetto antico una riproduzione brillante :-)


3
@Ewan IMHO "non funziona ma sembra carino" è qualcosa che dovrebbe essere riservato esclusivamente al settore della moda e non all'IT. IMHO git dovrebbe rimuoverlo perché ovviamente incoraggia troppe persone a pensare che lo stile sia più importante della sostanza.
gbjbaanb,

12
Vorrei contestare il tuo suggerimento che mantenere la storia pulita è inutile. Non è. Una cronologia del repository è destinata al consumo umano e come tale ha molto valore nel renderla leggibile. Nel mio caso d'uso particolare, intendiamo addirittura che i Project Manager non sviluppatori siano in grado di leggere la cronologia. Una cronologia chiara può solo aiutare con questo, e può aiutare un nuovo sviluppatore che non ha mai visto la base di codice e deve immergersi e correggere un bug per capire cosa è successo in una particolare area di codice.
jpmc26,

3
Il codice esiste innanzitutto e deve essere eseguito da una macchina (possibilmente dopo essere stato compilato). Applicando la tua filosofia sopra, concluderemmo che la leggibilità del codice non ha importanza perché nessuna leggibilità risolverà la difficoltà di comprensione del codice. Non ho mai suggerito che qualcosa dovrebbe essere schiacciato. Sto solo dicendo che un grafico complesso che si discosta in molti percorsi di attraversamento è difficile da seguire, quindi vale la pena investire un po 'per mantenere la tua storia abbastanza pulita. Nota anche che una storia lineare è più favorevole a sfruttare strumenti git come la colpa e il bisect.
jpmc26,

6
I documenti non dicono "non rebase". Dice "non rifare i cambiamenti a monte delle altre persone ". C'è un mondo di differenza tra quei due. Lo stesso Linus consiglia alcuni rifacimenti per l'organizzazione della storia, come si può vedere nel link nella risposta accettata. E come farebbe uno strumento a sapere quali commit sono irrilevanti (specialmente se gli strumenti hanno difficoltà ad analizzare una storia non lineare!), E come faresti a sapere con quale impegno vuoi diffondere (specialmente senza essere in grado di scavare nella storia!)? Stai mettendo in guardia su una situazione specifica ad un estremo dogmatico.
jpmc26,

4
Non vorrai assolutamente "mantenere la cronologia delle revisioni" se ciò include ogni commit che uno sviluppatore ha mai fatto. Seguendo questa logica dovremmo anche registrare la versione originale del codice ogni volta che viene premuto il tasto backspace. Ogni commit dovrebbe essere un cambiamento minimo e completo che mantiene comunque l'intero progetto in uno stato stabile. Se il commit originale viene successivamente rilevato come errato, è molto meglio rifare / modificare / modificare il commit piuttosto che crearne un altro. Tuttavia, vedi anche mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html
Mikko Rantalainen

12

Hai ragione, supponendo di avere un solo repository locale / modificato. Tuttavia, considera l'esistenza di un secondo PC locale, ad esempio.

Una volta che la tua copia locale / modificata viene spinta da qualche altra parte, la reimpostazione rovinerà quelle copie. Certo, potresti forzare la spinta, ma questo diventa rapidamente complicato. Cosa succede se uno o più di questi hanno un altro commit completamente nuovo?

Come puoi vedere, è molto situazionale, ma la strategia di base mi sembra (molto più pratica) in casi non speciali / di collaborazione.


Una seconda differenza: la strategia di unione manterrà una struttura chiara e coerente nel tempo. Dopo i rinnovi è molto probabile che gli impegni più vecchi possano seguire i cambiamenti più recenti, rendendo più difficile comprendere l'intera storia e il suo flusso.


1
"Dopo le ribattezze è molto probabile che gli impegni più vecchi possano seguire i cambiamenti più recenti", ma ciò accade anche con le fusioni, motivo per cui potresti (forse) avere bisogno del bel grafico per capirlo. Il momento in cui è stato effettuato un commit potrebbe essere molto prima della fusione che lo ha portato in questo ramo.
Steve Jessop,

6

Il grande motivo è probabilmente che il comportamento predefinito dovrebbe "funzionare" nei repository pubblici. Reimpostare la storia che altre persone hanno già unito sta causando loro problemi. So che stai parlando del tuo repository privato, ma in generale git non sa né si preoccupa di ciò che è privato o pubblico, quindi il valore predefinito scelto sarà il valore predefinito per entrambi.

Uso git pull --rebaseun bel po 'nel mio repository privato, ma anche lì ha un potenziale svantaggio, che è che la mia storia HEADnon riflette più l'albero mentre ci lavoravo davvero.

Quindi, per un grande esempio, supponiamo che io esegua sempre dei test e assicurassi che passino prima di eseguire un commit. Questo ha meno rilevanza dopo che ho fatto un git pull --rebase, perché non è più vero che l'albero in ciascuno dei miei commit ha superato i test. Finché le modifiche non interferiscono in alcun modo e il codice che estraggo è stato testato, presumibilmente avrebbe superato i test, ma non sappiamo perché non l'ho mai provato. Se l'integrazione continua è una parte importante del flusso di lavoro, qualsiasi tipo di rebase in un repository che viene sottoposto a CI è preoccupante.

Non mi dispiace davvero, ma disturba alcune persone: preferirebbero che la loro storia in git riflettesse il codice su cui effettivamente lavoravano (o forse quando lo spingono, una versione semplificata della verità dopo un certo uso di "correzione").

Non so se questo problema, in particolare, sia il motivo per cui Linus ha scelto di unire piuttosto che di ridisegnare per impostazione predefinita. Potrebbero esserci altri svantaggi che non ho riscontrato. Ma dal momento che non è timido nell'esprimere la sua opinione nel codice, sono abbastanza sicuro che si riduce a quello che pensa sia un flusso di lavoro adatto per le persone che non vogliono pensarci troppo (e specialmente quelli che lavorano in un pubblico piuttosto di un repository privato). Evitare le linee parallele nel bel grafico, a favore di una linea retta pulita che non rappresenta lo sviluppo parallelo come è accaduto, probabilmente non è la sua priorità assoluta anche se è la tua :-)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.