2013-05-04 17 views
12

Abbiamo due repository Subversion, ciascuno con un singolo progetto. Quindi:Unisci due repository SVN separati in un unico repository Git

svn://server/svn/project_a 
svn://server/svn/project_b 

Sono progetti separati, e sono in repository separati con completamente separati commettere storie. Il Progetto A ha r1, r2, ... r100 e il Progetto B ha r1, r2, ... r400

Alla fine vorremmo unire questi due repository SVN in un unico repository Git. Sia che la fusione può avvenire in Git, o dovrebbe avvenire in un terzo repository SVN temporanea in primo luogo, in ultima analisi, vogliamo vedere:

git://server/svn/projects/ 

Che è un repository sia con Progetto A e Project B. Essi saranno conservati in cartelle separate, come:

git://server/svn/projects/project_a 
git://server/svn/projects/project_b 

Quindi non ci saranno conflitti "fondere" i due. Siamo stati in grado di utilizzare this answer in modo impeccabile per trasferire un singolo progetto SVN in un singolo progetto Git, con inclusa la cronologia del commit.

Vorremmo che unisse i nostri due progetti SVN A e B in un unico repository Git, ma desideriamo che i commit vengano uniti per data. es .:

8b8dad: Project A, r1 (first commit in Git) 
dbdffe: Project B, r1 (child of previous) 
0ae7f7: Project B, r2 ... 
615b51: Project A, r2 ... 
916e59: Project A, r3 ... 
85f241: Project B, r3 ... 

È possibile? Dovremmo unire i due repository SVN in uno, quindi importare in Git? O è più facile lasciarli separati ed eseguire l'unione durante l'importazione Git?

+0

Sono i repository SVN sia del tutto lineare (vale a dire senza rami)? –

risposta

3

Ecco cosa abbiamo finito per fare:

Fase 1: Unire i repository SVN in una temporanea SVN Repository

Ciò richiede l'accesso al repository SVN (non le copie di lavoro):

Innanzitutto, creare file di dump di ogni repository che si desidera unire:

svnadmin dump project_a > dumps/a.dmp 
svnadmin dump project_b > dumps/b.dmp 
svnadmin dump project_c > dumps/c.dmp 

Poi, cre mangiato un nuovo repository che ospiterà i repository unite:

svnadmin create svn-temp-project 

noti che è necessario checkout questo repository in una copia di lavoro, e creare le sottodirectory di progetto, o il carico delle vostre discariche non funziona:

svn co file:///var/svn/svn-temp-project svn-temp-project-wc 
cd svn-temp-project-wc 
mkdir project_a 
mkdir project_b 
mkdir project_c 
svn add . --force 
svn ci -m "Added initial project directories." 

Quindi, è possibile caricare ogni file di immagine singola nel proprio specifico (!!) directory del progetto:

svnadmin load svn-temp-project --parent-dir project_a < dumps/a.dmp 
svnadmin load svn-temp-project --parent-dir project_b < dumps/b.dmp 
svnadmin load svn-temp-project --parent-dir project_c < dumps/c.dmp 

si dispone ora di un repository SVN 3-fusione.

Fase 2: Migrazione del repository SVN 3-fusione in un repository Git

Le seguenti operazioni possono essere eseguite su una macchina locale - non ha bisogno di prendere posto sul server.

Innanzitutto, creare un file authors.txt che git-svn possa utilizzare per determinare l'autore di ciascun commit. Ho usato:

someguy = Some Guy <[email protected]> 
... 
(no author) = no_author <[email protected]_author> 

Con questa autori depositare in atto, si può quindi:

cd projects/ 
mkdir my-git-repository 
cd my-git-repository 
git svn init https://svn.mycompany.com/svn/svn-temp-project --no-metadata 
git config svn.authorsfile ../authors.txt 
git svn fetch 

Fase 3: Pulizia

Questo metodo funziona bene per la fusione commettere storia, ma si finisce con le directory simili a SVN:

repo/project_a/trunk 
repo/project_a/branches 
repo/project_a/tags 
repo/project_b/trunk 
repo/project_b/branches 
repo/project_b/tags 
... 

Quindi, prima di premere, è necessario migrare tutti i tag/rami a Git. Non l'abbiamo fatto. I nostri tag non erano necessari da tenere in giro, poiché avevamo altre fonti per recuperarli e non avevamo alcun ramo per questi progetti.

Dopo aver rimosso le directory branches e tags, abbiamo ridotto il contenuto di trunk/ a un livello, quindi tutto era al livello "root" specifico del progetto.

+0

Questo mantiene correttamente la cronologia dei commit per tutti i repository di base SVN? –

+0

@JonathonReinhart Sì, nella nostra esperienza, lo fa. –

3

Ecco cosa farei in una shell Linux (non testata):

  1. convertire ciascuno per la propria git repo
  2. fare un terzo repo git con una vuota primo commit

    git ci --allow-empty -m'Add empty, initial commit'

  3. nel repository vuoto, aggiungere ogni repo come telecomando

    git remote add repoA 'path/to/git/repoA'
    git remote add repoB 'path/to/git/repoB'

  4. recuperare i repos in quella vuota (questo ottiene tutti gli oggetti in un repo)

    git fetch repoA
    git fetch repoB

  5. ottenere un elenco di commit in ogni repo prefisso Timestamp Unix (secondi dall'1/1/1970)

    git --no-pager log --format='%at %H' master >repoACommits
    git --no-pager log --format='%at %H' master >repoBCommits

  6. gatto entrambi in un unico, ordinato (da timestamp) Lista, abbattimento i timestamp:

    cat repoACommits repoBCommits | sort | cut -d' ' -f2 >orderedCommits

  7. nel vostro nuovo pronti contro termine, scorrere l'elenco, cherry-picking ogni (presumibilmente per padroneggiare)

    git co master
    cat orderedCommits | while read commit; do git cherry-pick $commit; done

Questo è tutto teorico, ma penso che funzionerà. Non so cosa succede se hai un conflitto di fusione tra i due. Non sono sicuro che lo while si fermerà o continuerà a provare e non riuscirà a continuare.

Ho appena notato che hai menzionato il desiderio di mantenere ciascuno nel lavoro del repository in cartelle separate nella cartella finale. Avrai bisogno del misterioso e potente git filter-branch per eseguire prima i singoli repo separatamente, facendo il lavoro di spostare oggetti aggiunti in una cartella, per commit. Probabilmente merita una nuova domanda, se non ha già risposto su SO.

+0

Gary - grazie per la tua risposta, sembra quasi perfetto, e lo proveremo presto. Per la tua menzione riguardo il mantenimento del lavoro di ciascun repository in cartelle separate nella cartella finale, ad es. 'projects/projectA' e' projects/projectB', sarebbe possibile impostare il telecomando Git in modo che punti (come destinazione) a una sottocartella specifica? In modo che il 'fetch' non si limita a scaricare entrambi i repository nella root? –

+0

No, non funzionerebbe. Git immagazzina "alberi", che sono elenchi di directory ricorsivi (1 file di testo per directory). Non c'è un modo semplice per indicare quelli da qualche altra parte durante questa procedura. È necessario filtrare i rami di ogni repository separato per creare una cartella e spostare tutto in esso prima di poter proseguire. L'ho appena testato localmente e ha funzionato: 'git filter-branch --tree-filter 'mkdir -p newfolder; find -mindepth 1 -maxdepth 1 -not -name newfolder -exec mv {} $ fname newfolder \; ' master' - cambia le 3 istanze di "newfolder" con qualsiasi nome desideri per la sottocartella di quel particolare repository. –

+0

Una volta fatto ciò, è possibile verificare facendo 'git whatchanged --oneline' - tutti i file elencati per commit dovrebbero avere il nomecomputer che li precede. * Quindi * è possibile ottenere i registri, cat/ordinarli e usarli per cherry-pick. Una nota sul cherry-picking - è possibile che tu abbia commette vuoti da qualche parte, il che farà crollare il comando cherry-pick che ho menzionato in origine. Aggiungi '--allow-empty' per superare questo dopo' cherry-pick'. –

5

Così ho provato il metodo di Craig, ma questo mi ha lasciato una storia un po 'insoddisfacente sul repository combinato alla fine. Ho trovato il checkout di tutti i repository svn in git separati e poi ramificandoli ha fatto una bella storia in cui si incontrano tre rami.

Quindi, per prima cosa fai il passaggio "autori" per creare autori.txt:

someguy = Some Guy <[email protected]> 
... 
(no author) = no_author <[email protected]_author> 

Ora è necessario verificare tutte le operazioni pronti contro termine svn usando git:

mkdir proja projb projc ... 

Ora dovete ripetere quanto segue per ogni progetto, e dal momento che i tuoi pronti contro termine non sono probabilmente una singola cartella fare un ulteriore commit:

cd proja 
git svn init https://svn.mycompany.com/svn/proja --no-metadata 
git config svn.authorsfile ../authors.txt 
git svn fetch 

#here comes the additional part: 
mkdir -p proja     #proja/proja 
git mv -k * proja    #move everything in there 
git commit -m "subtree proja" 

poi sono andato e ha reso il mio nuovo repo combinato in cui ho usato un ramo diverso per ogni sottoprogetto:

mkdir ../superproj 
cd ../supeproj 
git init 
git commit --allow-empty  #so that we have a master branch 
git branch proja projb projc... 

I seguenti deve essere ripetuta per ogni sotto-progetto:

git checkout proja 
git remote add proja_rm ../proja 
git pull proja_rm    #probably add a branch (e.g. master) 
git remote rm proja_rm   #cleanup 

Infine è possibile combinare il tutto in tuo padrone

git checkout master 
git merge proja projb projc... #it all comes together 
git push whereeveryouwant 
+0

nota che in git 2.9 dovresti aggiungere '--allow-unwrel-histories' quando fai unire dove" #tutti si uniscono " –

Problemi correlati