In che modo Git risolve il problema della fusione?

SVN ha reso molto più semplice la ramificazione, rendendo le filiali davvero economiche, ma le fusioni rimangono un vero problema in SVN, una soluzione che Git risolve presumibilmente.

Git raggiunge questo, e come?

(disclaimer: tutto ciò che so su Git è basato sulla lezione di Linus – qui git noob totale)

Git non impedirà il conflitto nelle unioni ma può riconciliare la cronologia anche quando non condivide alcun antenato genitore.
(tramite il file di grafts ( .git/info/grafts ) , che è una lista, una per riga, di un commit seguito dai suoi genitori, che puoi modificare per quello scopo di “riconciliazione”.)
Quindi piuttosto potente proprio lì.

Ma per dare uno sguardo a “come sono state fuse le fusioni”, puoi iniziare rivolgendoti a Linus stesso e rendersi conto che questo problema non riguarda tanto l ‘”algoritmo”:

Linus : Io personalmente , voglio avere qualcosa che sia molto ripetibile e non intelligente. Qualcosa che capisco o mi dice che non può farlo.
E francamente, unire la cronologia dei singoli file senza tenere conto della cronologia di tutti gli altri file mi fa andare “ugh”.

La parte importante di un’unione non è il modo in cui 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 le future fusioni .

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

E sembra che il 99% delle persone SCM sembrino pensare che la soluzione sia quella di essere più intelligenti riguardo alla fusione dei contenuti. Che manca completamente il punto.


Così Wincent Colaiuta aggiunge (sottolineatura mia):

Non sono necessari metadati elaborati, rinominare il tracciamento e così via.
L’unica cosa che devi 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, cancellazioni, rinomina, modifiche ecc.) Sono implicitamente codificato nel delta tra i due stati dell’albero ; ti basta tracciare qual è il contenuto .

Assolutamente tutto può (e dovrebbe) essere dedotto .

Git rompe gli schemi perché pensa al contenuto, non ai file.
Non tiene traccia dei nomi, tiene traccia dei contenuti. 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 esegui un diff, stai confrontando due alberi, non due file.

L’altra decisione progettuale 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.
Dovrebbe essere così. 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.

Ora è generalmente d’accordo su quell’algoritmo di fusione a 3 vie (magari con miglioramenti come rinominare il rilevamento e gestire una storia più complicata), che tiene conto della versione sul ramo corrente (‘nostra’), della versione sul 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 storia completa tra quei rami. Ciò che mancava a Subversion prima (corrente) della versione 1.5 (senza strumenti di terze parti come SVK o svnmerge) era il tracciamento delle unioni, ovvero ricordare per un merge il commit di quali genitori (che cosa commettevano) erano usati in unione. Senza questa informazione non è ansible calcolare correttamente l’antenato comune in presenza di fusioni ripetute.

Prendi per conto il seguente diagramma:

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

(che probabilmente verrebbero maciullati … sarebbe bello avere la possibilità di disegnare diagrammi artistici ASCII 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 rendevano difficile l’unione perché dovevi calcolare tu stesso l’antenato comune e dare manualmente informazioni sull’antenato quando esegui 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’object commit. In questo modo puoi dire che Git memorizza DAG (grafico aciclico diretto) delle revisioni, memorizzando e ricordando le relazioni tra i commit.


(Non sono sicuro di come Subversion tratti i problemi menzionati di seguito)

Inoltre la fusione in Git può affrontare due ulteriori problemi di complicazione: la rinomina dei file (quando una parte rinominò un file, e altri no; vogliamo ottenere il cambio di nome, e vogliamo ottenere le modifiche applicate al file corretto) e le unioni incrociate (Storia più complicata, quando c’è più di un antenato comune).

  • Le ridenominazioni dei file durante l’unione vengono gestite utilizzando il punteggio di similarità euristico basato (sia la somiglianza tra il contenuto del file e la somiglianza del nome del percorso che viene preso in considerazione) che rinominano il rilevamento . Git rileva quali file corrispondono tra loro in rami uniti (e antenati). In pratica funziona abbastanza bene per casi reali.
  • Incroci incrociati , vedere la definizione su wiki revctrl.org (e la presenza di più basi di unione ) sono gestiti utilizzando la strategia di unione ricorsiva , che genera un singolo antenato virtuale virtuale.

Le risposte di cui sopra sono corrette, ma penso che manchino per me il punto centrale delle facili fusioni di git. Un’unione SVN richiede che tu tenga traccia e ricordi cosa è stato unito e che è un enorme PITA. Dai loro documenti:

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

Ora non è un killer, ma se dimentichi se è 23-30 compreso o 23-30 esclusivi, o se hai già unificato alcuni di questi commit, sei un hosed e devi trovare le risposte per evitare ripetere o mancare di commit. Dio ti aiuti se si dirama un ramo.

Con git è solo git merge e tutto questo avviene senza problemi, anche se hai scelto una coppia di commit o fatto un numero qualsiasi di cose fantastiche di git-land.

Per quanto ne so, gli algoritmi di fusione non sono più intelligenti di quelli degli altri sistemi di controllo delle versioni. 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.

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 alla fusione perché tutto è fatto localmente ed è scritto in C.

SVN, correttamente utilizzato, è perfettamente utilizzabile.