2010-06-18 4 views
16

Come la maggior parte delle persone nuove a Git, ho avuto la mia parte di confusione cercando di decifrare i casi d'uso applicabili a git merge e git rebase. Penso di aver finalmente deciso che, per quanto riguarda lo stato di copia di lavoro risultante, ti danno la stessa cosa. Inoltre, entrambi provocano gli stessi conflitti. Se questo non è corretto, si prega di fornire un esempio per illuminarmi.git: throwaway unisce e git-rerere: qual è il punto?

Dal mio punto di vista, il vantaggio principale di usare rebase invece di unire (se le modifiche non sono state inserite o tirate) è di mantenere lineare la cronologia. Quello che davvero non capisco è il ragionamento che sta dietro lo sviluppo di git-rerere.

Dalla manpage, si suppone che git-rerere ti aiuti nel caso in cui tu stia cercando di risolvere un conflitto che hai precedentemente risolto. L'esempio a cui mi riferisco si trova a http://www.kernel.org/pub/software/scm/git/docs/git-rerere.html.

Se la politica del progetto non fonde continuamente le modifiche dalla linea principale al ramo dell'argomento (come il kernel di Linux), l'esempio sopra riportato dice di creare commit "throw-away". In sostanza, unisci il master al tuo argomento, esegui test per assicurarti che tutto funzioni ancora, quindi esegui un "git reset --hard HEAD ^", in pratica lanciando il merge commit away. In seguito, quando crei un altro commit "throw-away" di merge, git-rerere ti assiste nel risolvere i conflitti che hai già risolto nella prima fusione di lancio.

Qualcuno può spiegare perché, invece di affrontare tutti i problemi legati alla creazione di un commit temporaneo di merge, lo sviluppatore non dovrebbe semplicemente rebase il suo argomento sul master? Non è questo il punto di git-rebase: ottieni le modifiche, ma evita l'unione? Questo non porta a compimento la stessa cosa, e supponendo che nessuno abbia tirato le modifiche ai tuoi topic topic, non sarebbe un approccio molto più semplice? Il flusso di lavoro throw-away-merge + git-rerere è davvero solo per il caso in cui i tuoi cambiamenti sono stati spinti/tirati?

Un'ultima domanda - Linus è citato come dicendo "Ma se vedo un sacco di 'Merge branch linus' nei tuoi log, non ho intenzione di strapparti, perché il tuo albero ha ovviamente avuto schifezze casuali in esso che non dovrebbe essere lì ... "Linus avrebbe anche un problema con i rebases costanti?

risposta

21

È corretto che il rebasing e l'unione possano produrre lo stesso albero finale. La storia che producono è molto diversa, tuttavia, e il controllo della versione riguarda ovviamente la storia. Hai espresso una preferenza per la storia lineare; questo è davvero auspicabile su scala locale, mentre le unzioni aiutano a registrare l'interazione su larga scala di funzionalità, correzioni di bug e così via.

La semplice risposta alla tua domanda centrale (perché non solo rebase) è che a volte c'è un posto giusto per iniziare un ramo, e se questo è il caso, dovresti unire, non rebase.

Ad esempio, è possibile avere un bugfix che si applica a una versione di manutenzione e alla versione corrente; vorresti dirigerlo dall'ultimo commit che è un antenato di entrambe le versioni. Non puoi mai ribattere quel ramo in avanti lungo il master o il ramo di manutenzione, altrimenti non saresti più in grado di unirlo all'altro.

Analogamente, tutti i rami degli argomenti iniziano da qualche parte. Ad un certo punto, vuoi conservare quella storia. Lei menziona uno dei casi chiari (altri hanno tirato il tuo lavoro), ma potrebbe essere qualcosa di molto meno ovvio. Forse c'è qualche interazione con altre funzionalità, forse questa funzionalità ha sottofunzioni e stai cercando di mantenere l'intera gerarchia preservata.

Quindi, certo, se il ramo è locale e non ci sono motivi per mantenere la sua base fissa, è come si dice molto più semplice per ridiscutere semplicemente e fare. Ma a volte non è la cosa giusta da fare.

La tua domanda finale riguarda in realtà qualcosa di molto diverso, niente a che fare con i conflitti di unione. Linus ha dichiarato che in the context di fusioni che non è stato necessario eseguire . Questo è molto un problema di filosofia di branca e fusione. Junio ​​Hamano ha scritto uno excellent blog post su questo stesso problema. Una breve citazione che riassume l'argomento a portata di mano:

quando si uniscono un ramo soggetto 'add-frotz' al vostro ramo 'master', si sta, ovviamente, che incorpora la nuova funzione 'frotz', ma ancora più importante, si sta affermando che è desiderabile avere quella caratteristica 'frotz' nel ramo 'master'

Linus non vuole vederti fondendo questo ramo strano chiamato 'Linus' per tutto il tempo, perché chiaramente si' non unire qualche argomento specifico che è auspicabile avere nel tuo ramo. Stai fondendo ripetutamente il ramo upstream nel ramo dell'argomento, che è completamente nella direzione sbagliata. Non hai bisogno di tutte le cose da master (o linus) per sviluppare il tuo argomento. Dovresti finire il tuo argomento, quindi unirlo in altro modo, a monte in master! Non è desiderabile avere tutto il contenuto del master nel ramo dell'argomento. (E il master di un singolo sviluppatore è in realtà un ramo tematico, per quanto riguarda l'integratore.)

Quindi, Linus non ha un problema con fusioni frequenti; ha un problema con fusioni senza scopo e controproducente. (E generalmente, se ti assicuri che le tue fusioni siano fatte per una buona ragione, non saranno frequenti - e non saranno quasi mai una fusione downstream.) Se i tuoi rebases sono fatti per una buona ragione, e fanno la storia meglio, quindi sono una buona cosa, per quanto frequenti.

+0

Grande, grazie per la risposta. Il commento bugfix - derivato dall'ultimo commit che è un antenato di entrambi, era in realtà molto illuminante. Sembra un'idea semplice, ma ho avuto l'impressione che uno dovrebbe diramarsi dalla punta di un ramo di maintentance, e quindi scegliere la correzione del bug sul master.Questo approccio sembra però molto più pulito e previene necessariamente la rebasing. – Josh

+0

@ user370562: Il principio qui è di unire upstream/upwards e di creare i tuoi rami in modo tale che sia possibile. (Per inciso, i branch di manutenzione abbastanza spesso possono essere riuniti direttamente in master, le correzioni di bug che hai fatto su v5 probabilmente si applicano ancora alla v6 in sviluppo.) La maggior parte dei suggerimenti dei workflow enfatizza la fusione verso l'alto - vedi 'man gitworkflows', per esempio . Felice di essere d'aiuto! – Cascabel

6

Questo non porta a termine la stessa cosa, e presumendo che nessuno abbia tirato le modifiche al ramo dell'argomento, non sarebbe un approccio molto più semplice? Il flusso di lavoro throw-away-merge + git-rerere è davvero solo per il caso in cui i tuoi cambiamenti sono stati spinti/tirati?

Hai colpito il chiodo sulla testa. La ridefinizione fa bene ai rami che non hai ancora pubblicato; rerere è utile per i rami che hai pubblicato (cioè i rami per i quali il rebasing sarebbe inappropriato/fastidioso per gli sviluppatori a valle).

Linus avrebbe anche un problema con rebases costanti?

No, perché rebasing non si traduca in merge commette, e, superficialmente, non si può vedere che un ramo è stato ricalcolato, mentre si può facilmente vedere se un ramo si è fusa in un altro ramo più volte.

0

branch per feature workflow raccomanda l'esistenza di un ramo di integrazione temporaneo. In tale scenario, se si rebase, si dovrebbe rebase (o pull) contro il ramo temporaneo che può avere diversi set di controllo nella versione finale. Rendere davvero inutile il tuo rebase. O del resto se hai fatto entrare il tuo ramo, quella attrazione potrebbe rivelarsi inutile.

Rerere tuttavia aiuterà notevolmente ad alleviare il ricorrente dolore di fusione.