2009-03-04 4 views
53

SVN ha reso molto più semplice la ramificazione, rendendo le filiali davvero economiche, ma le fusioni rimangono un problema reale in SVN, che Git presumibilmente risolve.In che modo Git risolve il problema della fusione?

Git raggiunge questo e come?

(disclaimer: Tutto quello che so su Git si basa sulla lezione Linus - totale git niubbo qui)

+8

In risposta a: "SVN ha reso la ramificazione molto più semplice rendendo i rami davvero economici". Sei sicuro di non aver accidentalmente sostituito Git con SVN? So che una delle grandi funzionalità che Git vanta è la ramificazione economica ... Ho sentito dire che la ramificazione in SVN è un incubo perché gran parte di essa è manuale (crea una nuova directory con contenuti ramificati, ecc.). – Stunner

risposta

73

Git non impedisce il conflitto nelle unioni ma può riconciliare la cronologia anche quando non condivide alcun antenato padre.
(tramite The grafts file (.git/info/grafts), che è un elenco, uno per riga, di un commit seguito dai suoi genitori, che è possibile modificare per tale scopo "riconciliazione".)
Così potente proprio lì.

Ma per davvero avere un assaggio su "come unioni sono state pensato attraverso", è can start by turning to Linus himself, e realizzare questo problema non è tanto di "algoritmo":

Linus: Me personalmente , Voglio avere qualcosa che sia molto ripetibile e non intelligente. Qualcosa che capisco o mi dice che non può farlo.
E francamente, fondendo la storia file singolo senza prendere la storia di tutti gli altri file in considerazione mi fa andare 'ugh'.

La parte importante di un'unione non è come gestisce i conflitti (che devono comunque essere verificati da un essere umano se sono comunque interessanti), ma che dovrebbe fondere insieme la cronologia giusta in modo da avere una nuova solida base per future fusioni.

In altre parole, la parte importante è la banale parte: la denominazione dei genitori, e tenere traccia della loro relazione. Non gli scontri.

e sembra che il 99% delle SCM persone sembrano pensare che la soluzione a questo è di essere più intelligente su unioni di contenuti. Che manca completamente il punto.


Così Wincent Colaiuta aggiunge (sottolineatura mia):

v'è alcuna necessità per i metadati di fantasia, rinominare il monitoraggio e così via.
L'unica cosa che è necessario memorizzare è lo stato dell'albero prima e dopo ogni modifica.

Quali file sono stati rinominati? Quali sono stati copiati? Quali sono stati cancellati? Quali linee sono state aggiunte? Quali sono stati rimossi? Quali linee hanno apportato modifiche al loro interno? Quali lastre di testo sono state copiate da un file all'altro?
Non dovresti preoccuparti di nessuna di queste domande e certamente non dovresti tenere dati di tracciamento speciali per aiutarti a rispondere: tutte le modifiche all'albero (aggiunte, cancella, rinomina, modifica ecc.) sono implicitamente codificati nel delta tra i due stati dell'albero; ti basta traccia qual è il contenuto.

Assolutamente tutto può (e dovrebbe) essere dedotto.

Git rompe lo stampo perché pensa al contenuto, non ai file.
Non traccia i nomi, rintraccia il contenuto. E lo fa a livello di un intero albero.
Questa è una partenza radicale dalla maggior parte dei sistemi di controllo delle versioni.
Non si preoccupa di provare a memorizzare le storie per file; memorizza invece la cronologia a livello di albero.
Quando si esegue un diff si stanno confrontando due alberi, non due file.

L'altra decisione di progettazione fondamentalmente intelligente è come Git si fonde.
Gli algoritmi di fusione sono intelligenti ma non cercano di essere troppo intelligenti. Le decisioni univoche vengono prese automaticamente, ma quando ci sono dei dubbi è l'utente a decidere.
Questo è il modo in cui dovrebbe essere. Non vuoi che una macchina prenda queste decisioni per te. Non lo vorresti mai.
Questa è l'intuizione fondamentale nell'approccio alla fusione di Git: mentre ogni altro sistema di controllo delle versioni sta cercando di diventare più intelligente, Git è felicemente descritto come lo "stupido gestore di contenuti", ed è meglio per questo.

+1

Questo mi sembra una funzione pensata per aiutarti a recuperare dagli errori del passato. Anche se è una cosa nobile e buona, in realtà non ti aiuta a non commettere l'errore. – RibaldEddie

+0

puoi spiegare ulteriormente cos'è un albero? git n00b qui. – hasen

+0

hansen_j, per ulteriori informazioni sugli alberi di git leggere http://www.newartisans.com/2008/04/git-from-the-bottom-up.html –

5

Per quanto ne so, gli algoritmi fusione non sono affatto più intelligenti di quelli di altri sistemi di controllo versione. Tuttavia, a causa della natura distribuita di git, non c'è bisogno di sforzi di fusione centralizzati. Ogni sviluppatore può rebase o unire piccole modifiche da altri sviluppatori nel suo albero in qualsiasi momento, quindi i conflitti che si presentano tendono ad essere più piccoli.

-10

Git rende solo più difficile rovinare il repository di tutti gli altri con una brutta unione.

L'unico vero vantaggio è che Git è molto, molto più veloce a fondere perché tutto è fatto a livello locale ed è scritto in C.

SVN, utilizzato correttamente, è perfettamente utilizzabile.

+2

Anche Git differisce in modo diverso. Guarda la differenza di contenuto, piuttosto che un file per le modifiche alla linea di file. – Saem

16

E 'ormai generalmente hanno concordato che l'algoritmo di fusione a 3 vie (magari con miglioramenti quali come il rilevamento di rinomina e trattare con la storia più complicata), che prende in versione considerazione sul ramo corrente ('la nostra'), versione on il ramo unito ("loro") e la versione di antenato comune dei rami uniti ("antenato") è (dal punto di vista pratico) il modo migliore per risolvere le unioni. Nella maggior parte dei casi, e per la maggior parte dei contenuti, il livello dell'albero si fonde (quale versione del file da prendere) è sufficiente; raramente c'è bisogno di gestire i conflitti dei contenuti, e quindi l'algoritmo diff3 è abbastanza buono.

Per utilizzare l'unione a 3 vie è necessario conoscere l'antenato comune dei rami uniti (co denominato base di unione). Per questo è necessario conoscere la cronologia completa tra quelle filiali. Ciò che mancava a Subversion prima (corrente) versione 1.5 (senza strumenti di terze parti come SVK o svnmerge) era tracciamento unione, vale a dire ricordare per l'unione di commit quali genitori (quali commit) sono stati utilizzati in unione. Senza questa informazione non è possibile calcolare correttamente l'antenato comune in presenza di fusioni ripetute.

Prendere per conto schema seguente:

---.---a---.---b---d---.---1 
     \  /
     \-.---c/------.---2 

(che sarebbe probabilmente ottenere straziati ... sarebbe bello avere la capacità di disegnare diagrammi ASCII-art qui).
Quando stavamo unendo i commit 'b' e 'c' (creando commit 'd'), l'antenato comune era il punto di ramificazione, commit 'a'. Ma quando vogliamo unire i commit '1' e '2', ora l'antenato comune è commit 'c'. Senza memorizzare le informazioni di fusione, dovremmo concludere erroneamente che è commit 'a'.

Subversion (precedente alla versione 1.5) e CVS precedente hanno reso difficile l'unione perché era necessario calcolare personalmente un antenato comune e fornire manualmente informazioni sull'antenato quando si esegue un'unione.

Git memorizza le informazioni su tutti i genitori di un commit (più di un genitore nel caso di un merge di commit) nell'oggetto commit. In questo modo puoi dire che Git memorizza DAG (grafico aciclico diretto) delle revisioni, memorizzando e ricordando le relazioni tra i commit.


(io non sono sicuro di come offerte di Subversion con le questioni di cui qui di seguito)

Inoltre si fondono in Git possono affrontare due questioni complicazione: file di rinomina (quando una parte rinominato un file e altro no, vogliamo ottenere il cambio di nome e vogliamo applicare le modifiche al file corretto) e si fonde in modo incrociato (cronologia più complessa, quando esiste più di un antenato comune).

  • File rinomina durante l'unione sono gestiti con il punteggio di somiglianza euristica base (sia similarità dei contenuti di file e somiglianza del percorso viene presa in considerazione) rename rilevamento. Git rileva quali file corrispondono tra loro in rami uniti (e antenati). In pratica funziona abbastanza bene per casi reali.
  • Criss-cross fonde, vedere definition at revctrl.org wiki, (e presenza di più basi unione) sono gestiti utilizzando ricorsiva strategia unione, che genera singolo antenato comune virtuale.
+1

Ho provato a migliorare il diagramma, formattandolo come una citazione di blocco ... Spero di non averlo rotto a causa di una comprensione insufficiente, mi scuso in tal caso. – unwind

9

Le risposte sopra sono tutte corrette, ma penso che manchino per me il punto centrale di Git. Un'unione SVN richiede che tu tenga traccia e ricordi cosa è stato unito e che è un enorme PITA. Dalle loro documenti:

svn merge -r 23:30 file:///tmp/repos/trunk/vendors 

Ora che non è assassino, ma se si dimentica che si tratti di 23-30 inclusive o 23-30 esclusivo, o se hai già incorporato le alcune di quelle commit, si si sta hosed e Devo trovare le risposte per evitare di ripetere o perdere i commit. Dio ti aiuti se si dirama un ramo.

Con git è solo git merge e tutto ciò avviene senza problemi, anche se hai selezionato una coppia o commesso un numero qualsiasi di cose fantastiche di git-land.

+2

Penso che ti stai dimenticando del tracciamento delle unioni che svn ha da poco. –

+3

è vero, non ho avuto molta esperienza con le nuove funzionalità di fusione. Da una certa distanza sembra un po '"una volta - la fusione integrale viene eseguita da un ramo all'altro, il ramo non è più utilizzabile per ulteriori lavori." Non è in grado di assorbire correttamente le nuove variazioni del tronco ... "meglio di niente certamente. – jdwyah