C'è una differenza tra le fusioni in svn rispetto a git o mercurial?


12

Da quanto ho capito, SVN è 'facile da ramificare. Difficile da unire '. Perché? C'è differenza nel modo in cui si fondono?

git  svn  mercurial  dvcs 

Risposte:


21

Si prega di consultare la mia risposta Stack Overflow per una situazione molto concreta in cui Mercurial (e Git) si fondono senza problemi e in cui Subversion presenta un falso conflitto. La situazione è un semplice refactoring eseguito su un ramo in cui si rinominano alcuni file.

Per quanto riguarda la risposta di tdammer, ci sono una serie di equivoci:

  • Subversion, Mercurial e Git tengono traccia delle istantanee del progetto a livello di repository. Chiamarli versioni , revisioni o changeset non fa differenza. Sono tutte istantanee logicamente atomiche di un insieme di file.

  • La dimensione dei tuoi commit non fa differenza quando si tratta di unire. Tutti e tre i sistemi si fondono con l'algoritmo di fusione standard a tre vie e gli input per tale algoritmo sono

    • la più grande versione di antenato comune
    • versione su un ramo
    • versione sull'altro ramo

    Non importa come sono state create le due versioni di diramazione. Puoi aver usato 1000 piccoli commit dalla versione precedente o puoi aver usato 1 commit. Tutto ciò che conta è la versione finale dei file. (Sì, questo è sorprendente! Sì, molte guide DVCS sbagliano terribilmente.)

Solleva anche alcuni punti positivi sulle differenze:

  • Subversion ha qualche "voodoo", dove è possibile unire da /trunkin, diciamo, /branches/foo. Mercurial e Git non usano questo modello - i rami sono invece modellati direttamente nella storia. La storia diventa quindi un grafico aciclico diretto invece di essere lineare. Questo è un modello molto più semplice di quello utilizzato da Subversion e questo elimina una serie di casi angolari.

  • Puoi facilmente ritardare un'unione o persino consentire a qualcun altro di gestirla. Se hg mergeti dà un sacco di conflitti, puoi chiedere al tuo collega di farlo hg pullda te e poi ha lo stesso identico stato. Quindi può hg mergee forse è più bravo a risolvere i conflitti di te.

    Questo è molto difficile con Subversion in cui è necessario aggiornare prima di poter eseguire il commit. Non puoi semplicemente ignorare le modifiche sul server e continuare a impegnarti nel tuo ramo anonimo. In generale, Subversion ti costringe a giocare con una sporca copia di lavoro quando lo fai svn update. Questo è un po 'rischioso poiché non hai archiviato le modifiche in modo sicuro. Git e Mercurial ti consentono di eseguire prima il commit, quindi di aggiornare e unire, se necessario.

La vera ragione per cui Git e Mercurial sono più bravi a fondersi rispetto a Subversion è una questione di implementazione. Ci sono conflitti di ridenominazione che Subversion semplicemente non può gestire, anche se è chiaro quale sia la risposta corretta. Mercurial e Git gestiscono quelli facilmente. Ma non vi è alcun motivo per cui Subversion non sia in grado di gestirli - l'essere centralizzato non è certamente il motivo.


4
Bella risposta! Voterei due volte se potessi. :) Aggiungo anche che il libro SVN a cui ti riferisci nella risposta SO ammette chiaramente che "La funzionalità di tracciamento della fusione di Subversion ha un'implementazione interna estremamente complessa ..." - questo da solo è un'indicazione abbastanza buona che la funzionalità non è affidabile
gnat

2
... e anche con quell'implementazione estremamente complessa non riesco a capire correttamente l'antenato comune in casi non semplici.
Jan Hudec,

A proposito di fusioni ritardate - non capirle - con SVN il mio collega può aggiornare a / checkout trunk e quindi unire dal mio ramo in esso.
Gill Bates,

@GillBates: sto parlando di una situazione in cui non hai avviato una filiale per il tuo lavoro, quando lavori trunkin SVN. Con un DVCS puoi impegnarti senza condividere, ma in SVN svn commitinfluenzerai direttamente gli altri che lavorano nello stesso ramo. Anche se noi due lavoriamo in una filiale in SVN, non posso impegnare il mio lavoro senza dovermi fondere immediatamente con il tuo lavoro. Ciò rende i commit un po 'spaventosi - che è una proprietà spaventosa per un sistema di controllo versione! :-)
Martin Geisler il

6

Il problema principale risiede nel modo in cui questi sistemi rappresentano una struttura di directory con versione.

Il concetto di base di Subversion attorno al quale ruota l'intero sistema è quello di una versione (o, in svn lingo, "revisione"): un'istantanea di un file a un certo punto. Fintanto che la cronologia è perfettamente lineare, tutto va bene, ma se è necessario unire le modifiche da due linee di sviluppo indipendenti, svn deve confrontare le versioni correnti di entrambi e quindi fare un confronto a tre vie tra l'ultima versione condivisa e le due versioni di testa. Le linee che appaiono cambiate in una delle teste, ma non nell'altra, possono essere facilmente risolte; le linee che si discostano esattamente allo stesso modo in entrambe le teste sono più difficili, ma di solito realizzabili; linee che si discostano in modi diversi sono ciò che fa dire a svn "Non riesco a capirlo, umano, per favore risolvilo per me."

Al contrario, git e mercurial tengono traccia dei changeset piuttosto che delle versioni. L'intero repository è un albero di changeset, ognuno a seconda di un parent, in cui un changeset parent può avere un numero qualsiasi di figli e la radice dell'albero rappresenta una directory vuota. In altre parole, git / hg dice "prima non avevo niente, poi è stata applicata questa patch, poi quella patch, ecc.". Quando è necessario unire due linee di sviluppo, git / hg non solo conosce l'aspetto di ogni testa e l'aspetto dell'ultima versione comune, ma sa anche come è avvenuta la transizione, consentendo una fusione molto più intelligente.

Un'altra cosa che semplifica l'unione in un DVCS è una conseguenza indiretta della separazione dei concetti di commit e pushe di consentire qualsiasi tipo di fusione incrociata tra due cloni dello stesso repository in qualsiasi momento. Con svn, le persone tendono a eseguire il commit di grandi changeset con modifiche spesso non correlate, poiché un commit è anche un aggiornamento del repository centrale che interessa tutti gli altri membri del team; se commetti una versione non funzionante, tutti si arrabbieranno con te. Poiché la maggior parte delle configurazioni coinvolge un server svn in rete, il commit comporta anche il pumping dei dati sulla rete, il che significa che il commit introduce un notevole ritardo nel flusso di lavoro (specialmente quando la tua copia di lavoro è obsoleta e devi prima estrarla). Con git e mercurial, il commit avviene localmente e, poiché entrambi sono molto efficienti nella gestione dei filesystem locali, di solito termina all'istante. Di conseguenza, le persone (una volta abituate) commettono piccole modifiche incrementali e, quando funziona, spingere una dozzina circa si impegna in una volta sola. Quindi, quando arriva il momento della fusione, SCM ha molte più informazioni dettagliate da seguire e può fare un lavoro migliore risolvendo i conflitti in modo sicuro e automatico.

E poi ci sono i bei dettagli che rendono le cose ancora più facili:

  • Puoi avere più teste e comunque impegnarti su entrambe; a differenza della sovversione, non è necessario combinare pull, update e merge prima di eseguire nuovamente il commit: le teste multiple restano così fino a quando non si sceglie di unire
  • Le directory non vengono trattate in modo speciale; invece, il percorso è considerato solo un grande nome di file e tutte le directory devono essere sempre alla stessa revisione. Ciò significa che non è possibile eseguire il voodoo di sovversione in cui le sottocartelle di un progetto si trovano a revisioni diverse, ma significa anche che la copia di lavoro ha meno probabilità di diventare un enorme disordine non gestibile e, cosa più interessante, una mossa non viene rappresentata come eliminazione -e-add (che si spezzerebbe completamente in svn se non fosse per i metadati di retrofit), ma semplicemente come rinominazione; se sposti un file, viene conservata tutta la sua cronologia; l'unione può anche applicare le modifiche al file spostato che sono state apportate a una versione non spostata dello stesso file dopo lo spostamento, in un altro ramo
  • Il più delle volte, in realtà non hai nemmeno bisogno di diramare: invece, devi solo clonare l'intero repository. La clonazione è economica, specialmente se viene eseguita sullo stesso filesystem e se decidi di voler sbarazzarti del clone, devi semplicemente eliminare la directory in cui vive e basta. Non hai nemmeno bisogno di usare hg o git per questo.
  • Esistono poche (se presenti) restrizioni su ciò che è possibile unire. Puoi avere sei cloni dello stesso repository e unire (o meglio, push o pull; spesso non è richiesta un'unione esplicita) da A a B, quindi da C a B, quindi da B a D, quindi da C a D, quindi da B A, D, E, in qualsiasi momento e tutte le volte che vuoi.
  • È possibile testare un'unione clonando uno dei repository che si desidera unire, quindi estraendolo dall'altro. Se fa quello che vuoi, puoi respingere il vero bersaglio, in caso contrario, butti via il clone e ricomincia da capo.

2
Devo menzionare alcune correzioni e aggiungere la risposta: 1. Le revisioni SVN sono per repository globali , la revisione rappresenta tutti i file in repo in qualche momento 2. La tecnologia di unione è sostanzialmente comune in SVN e DVCS - se il file nei file di unione ha cambiato solo il allo stesso modo , l'unione produrrà la stessa quantità di conflitti per SVN e DVCS - tutti gli SCM funzionano ancora a livello di stringa, non un blocco logico 3. I grandi commit in SVN non sono il risultato di debolezza architettonica, ma perché gli utenti sono spesso idioti pigri - ignorano i modelli di base. Branch | merge funziona in SVN, se / dev / brain e / dev / hands funzionano
Lazy Badger

2
Parte 2: l'unione intelligente in DVCS si verifica principalmente perché, contrariamente a Subversion, tengono traccia e gestiscono le mosse | i nomi dei file e SVN non lo fanno affatto, quindi - qualsiasi operazione, che elabora i file, è cambiata su un lato e rinominata su secondo, fallirà. Ramificare con i rami e con la clonazione sono solo diverse strategie di ramificazione con gli stessi diritti di vita, che usare "... dipende da ..."
Lazy Badger,

@LazyBadger: Au contraire. Subversion tiene traccia delle mosse / rinominazioni, il che causa conflitti spuri in parte perché la gestione delle rinominazioni in unione è semplicemente errata e in parte perché ci sono casi angolari che sono difficili o impossibili da gestire correttamente. Il secondo è il motivo per cui git (e mercurial lo hanno copiato) di progettazione non tiene traccia dei nomi e li indovina quando si fondono. Il che funziona bene se il contenuto è ancora abbastanza simile per essere unito (che è quando ne hai bisogno) e non fa cose stupide in caso contrario.
Jan Hudec,

@JanHudec - scusate, l'handle SVN non si sposta | rinomina all'azione atomica (modo DVCS - "rinomina"), ma come "cancella + ...", quindi - produce conflitti tra alberi, dove non accade in DVCS ( rinominazione vera ). La traccia mercuriale rinomina esplicitamente ( hg mvo hg addremove --similarity...), mentre Git usa l'euristica, ma entrambi gestiscono le ridenominazioni . Posso avere un conflitto tra alberi anche con 1 differenza di stringa nei file uniti! Devi riapprendere alcuni aspetti di Subversion, scusa.
Lazy Badger,

5
Ora stiamo diventando abbastanza tecnici :-) Sia Subversion che Mercurial tracciano copie , non rinominazioni. Entrambi i sistemi tracciano rename a bcome copy a b; remove aed entrambi lo fanno in un commit atomico. La differenza nel comportamento di unione deriva dalla diversa gestione dei casi angolari e da Subversion che consente più fusioni rispetto a Mercurial e Git. Infine, Git rileva i nomi durante l'unione e il tempo di log - stiamo pensando di aggiungere anche questo in Mercurial.
Martin Geisler,
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.