Pousser un référentiel Git existant vers SVN


385

J'ai fait tout mon travail dans Git et j'ai poussé vers GitHub. Je suis très satisfait du logiciel et du site, et je ne souhaite pas changer mes pratiques de travail à ce stade.

Mon conseiller de doctorat demande à tous les étudiants de conserver leur travail dans un référentiel SVN hébergé à l'université. J'ai trouvé des tonnes de documentation et de tutoriels sur le point de dérouler un référentiel SVN existant dans Git, mais rien sur le fait de pousser un référentiel Git vers un nouveau référentiel SVN. Je m'attends à ce qu'il y ait un moyen de le faire avec une combinaison de git-svn et une nouvelle branche et rebasage et tous ces termes merveilleux, mais je suis un débutant Git et je ne me sens en confiance avec aucun d'entre eux.

Je veux ensuite simplement exécuter quelques commandes pour pousser les validations vers ce référentiel SVN lorsque je le souhaite. Je souhaite continuer à utiliser Git et avoir juste le miroir de dépôt SVN ce qui est dans Git.

Je serai la seule personne à s'engager sur SVN, si cela fait une différence.


1
À noter: vous perdrez probablement vos timbres-date d'origine lorsque vous effectuez cette opération. Les nouvelles dates seront basées sur l'heure de l'importation dans Subversion.
nobar

Pour ceux qui recherchent des informations plus récentes, certains peuvent trouver le post suivant utile lors de leur recherche vers l'automatisation: deliciousbrains.com/deploying-wordpress-plugins-travis
Josh Habdas

Réponses:


403

J'en avais également besoin, et avec l'aide de la réponse de Bombe + quelques bidouilles, je l'ai fait fonctionner. Voici la recette:

Importer Git -> Subversion

1. cd /path/to/git/localrepo
2. svn mkdir --parents protocol:///path/to/repo/PROJECT/trunk -m "Importing git repo"
3. git svn init protocol:///path/to/repo/PROJECT -s
4. git svn fetch
5. git rebase origin/trunk
5.1.  git status
5.2.  git add (conflicted-files)
5.3.  git rebase --continue
5.4.  (repeat 5.1.)
6. git svn dcommit

Après # 3, vous obtiendrez un message cryptique comme celui-ci:

Utilisation d'un niveau d'URL supérieur: protocol:///path/to/repo/PROJECT => protocol:///path/to/repo

Ignorez cela.

Lorsque vous exécutez # 5, vous pouvez rencontrer des conflits. Résolvez ces problèmes en ajoutant des fichiers avec l'état "non fusionné" et en reprenant le rebase. Finalement, vous aurez terminé; puis synchronisez à nouveau vers le référentiel SVN, en utilisant dcommit. C'est tout.

Garder les référentiels synchronisés

Vous pouvez maintenant synchroniser de SVN vers Git, en utilisant les commandes suivantes:

git svn fetch
git rebase trunk

Et pour synchroniser de Git vers SVN, utilisez:

git svn dcommit

Note finale

Vous voudrez peut-être essayer cela sur une copie locale, avant de postuler à un référentiel actif. Vous pouvez faire une copie de votre référentiel Git dans un emplacement temporaire; utilisez simplement cp -r, car toutes les données sont dans le référentiel lui-même. Vous pouvez ensuite configurer un référentiel de test basé sur des fichiers, en utilisant:

svnadmin create /home/name/tmp/test-repo

Et vérifiez une copie de travail en utilisant:

svn co file:///home/name/tmp/test-repo svn-working-copy

Cela vous permettra de jouer avec les choses avant d'apporter des changements durables.

Addendum: si vous vous trompez git svn init

Si vous exécutez accidentellement git svn initavec la mauvaise URL et que vous n'êtes pas assez intelligent pour effectuer une sauvegarde de votre travail (ne demandez pas ...), vous ne pouvez pas simplement exécuter à nouveau la même commande. Vous pouvez cependant annuler les modifications en émettant:

rm -rf .git/svn
edit .git/config

Et supprimez la section [svn-remote "svn"]section.

Vous pouvez ensuite git svn initrecommencer.


2
Bonne réponse. Est-ce que cela gâche également les dates de validation?
Drew Noakes

4
Bonnes questions - Malheureusement, je ne connais pas non plus la réponse. Il s'agit plus d'un guide pratique de ce que j'ai trouvé efficace. Je ne comprends pas tous les détails. En ce qui concerne les dates de validation, je suppose que vous pourriez faire un test et le découvrir. N'oubliez pas que vous pouvez initier un dépôt svn local (basé sur fs) pour tester les choses.
troelskn

10
J'ai suivi ces mêmes étapes en utilisant "git rebase --onto trunk --root" à la place de l'étape 5 et j'ai eu beaucoup plus de succès. Seule une poignée de conflits de fusion à résoudre, au lieu de tonnes.
kubi

5
Dans mon cas, cette séquence n'a pas fonctionné. Un message "Impossible de déterminer les informations SVN en amont à partir de l'historique HEAD" a toujours été affiché. Donc, aucun engagement possible.
Fedir RYKHTIK

2
Peu importe, j'ai essayé d'être intelligent et d'omettre le -s car je ne voulais pas suivre la configuration standard de SVN (trunk / branches / tags /). Je ne pourrais pas le faire fonctionner sans ça.
James McMahon

33

Voici comment nous l'avons fait fonctionner:

Clonez votre référentiel Git quelque part sur votre machine.

Ouvrez .git / config et ajoutez ce qui suit (dans Maintenance d'un miroir SVN en lecture seule d'un référentiel Git ):

[svn-remote "svn"]
    url = https://your.svn.repo
    fetch = :refs/remotes/git-svn

Maintenant, à partir d'une fenêtre de console, saisissez-les:

git svn fetch svn
git checkout -b svn git-svn
git merge master

Maintenant, s'il se casse ici pour une raison quelconque, tapez ces trois lignes:

git checkout --theirs .
git add .
git commit -m "some message"

Et enfin, vous pouvez vous engager sur SVN:

git svn dcommit

Remarque: je supprime toujours ce dossier par la suite.


6
+1 cela a réellement fonctionné pour moi (ne pas avoir de tronc / base quoi que ce soit), contrairement à d'autres réponses qui ont continué à donnerUnable to determine upstream SVN information from HEAD history.
stijn


2
J'ai essayé cette technique, mais elle n'a pas importé l'historique. Btw, "git merge master" est désormais "git merge --allow-unrelated-histories master"
Berend de Boer

1
Si vous voulez absolument remplacer ce qui est dans SVN par ce qui est dans git, utilisez git merge -s recursive -Xtheirs --allow-unrelated-histories master.
user1475814

28

En utilisant git rebase directe perdra le premier commit. Git le traite différemment et ne peut pas le rebaser.

Il existe une procédure qui conservera l'historique complet: http://kerneltrap.org/mailarchive/git/2008/10/26/3815034

Je vais transcrire la solution ici, mais les crédits sont pour Björn.

Initialisez git-svn:

git svn init -s --prefix=svn/ https://svn/svn/SANDBOX/warren/test2

Le --prefix vous donne des branches de suivi à distance comme "svn / trunk" ce qui est bien parce que vous n'obtenez pas de noms ambigus si vous appelez votre branche locale juste "trunk". Et-s est un raccourci pour la disposition standard de tronc / tags / branches.

Récupérez le contenu initial de SVN:

git svn fetch

Recherchez maintenant le hachage de votre commit racine (devrait montrer un seul commit):

git rev-list --parents master | grep '^.\{40\}$'

Ensuite, récupérez le hachage de la validation du tronc vide:

git rev-parse svn/trunk

Créez le greffon:

echo <root-commit-hash> <svn-trunk-commit-hash> >> .git/info/grafts

Maintenant, "gitk" devrait apparaître svn/trunkcomme le premier commit sur lequel votre branche principale est basée.

Rendre la greffe permanente:

git filter-branch -- ^svn/trunk --all

Laissez tomber la greffe:

rm .git/info/grafts

gitk devrait toujours apparaître svn/trunkdans l'ascendance du maître.

Linéarisez votre histoire au sommet du coffre:

git svn rebase

Et maintenant, "git svn dcommit -n" devrait vous dire qu'il va s'engager dans le tronc.

git svn dcommit

Pouvez-vous expliquer plus clairement comment cette technique est différente de ce qui précède.
cmcginty

3
Lorsque j'essaie "git rev-parse svn / trunk", il signale une révision inconnue ou un chemin qui ne se trouve pas dans l'arborescence de travail.
Adam Ness

C'est la seule réponse qui a fonctionné pour moi, sauf que les étapes git filter-branch et drop graft n'étaient pas nécessaires: j'ai fait un rebase après avoir créé le greffon, puis j'ai fait git svn dcommit.
fc7

8

Créez un nouveau répertoire dans le référentiel Subversion pour votre projet.

# svn mkdir --parents svn://ip/path/project/trunk

Passez à votre projet géré par Git et initialisez git-svn.

# git svn init svn://ip/path/project -s
# git svn fetch

Cela créera un seul commit car votre répertoire de projet SVN est toujours vide. Maintenant, rebasez tout sur ce commit, git svn dcommitet vous devriez avoir terminé. Cependant, cela gâchera sérieusement vos dates de validation.


J'ai utilisé cette réponse avec les instructions sur hassox.blogspot.com/2007/12/using-git-with-svn.html J'ai suivi ces commandes, puis "#git branch -a" pour voir le nom du tronc. Ensuite: # git checkout -b local-svn trunk # git merge master # git svn dcommit N'oubliez pas de .gitignore le répertoire .svn!
cflewis

Comme je viens de faire la même opération, je voulais préciser que depuis un certain temps (janvier '09), git peut effectuer une opération de rebase sur le commit racine. Cela rend le processus beaucoup plus simple que la plupart des anciens articles l'indiquent, voir les commentaires sur its.arubything.com/2009/1/4/…
Louis Jacomet

Que fait l'option d'initialisation "-s" git svn? Je ne peux pas voir cela dans les pages de manuel de git svn.
Nathan

Relisez la page de manuel, recherchez peut-être «-s» car elle est là. C'est un alias pour «--stdlayout».
Bombe

7

Git -> SVN avec l'historique complet des validations

J'avais un projet Git et je devais le déplacer vers SVN. C'est comme ça que je l'ai fait, en gardant tout l'historique des commit. La seule chose qui est perdue est l'heure de validation d'origine puisque libSVN définira l'heure locale lorsque nous le ferons git svn dcommit.

Comment:

  1. Avoir un dépôt SVN où nous voulons importer nos trucs et les cloner avec git-svn:

    git svn clone https://path.to/svn/repository repo.git-svn`
    
  2. Va là-bas:

    cd repo.git-svn
    
  3. Ajoutez la télécommande du référentiel Git (dans cet exemple, j'utilise C: /Projects/repo.git ). Vous voulez pousser vers SVN et lui donner le nom old-git:

    git remote add old-git file:///C/Projects/repo.git/
    
  4. Récupérez les informations de la branche principale du référentiel old-git vers le référentiel actuel:

    git fetch old-git master
    
  5. Récupérez la branche principale de la télécommande old-git dans une nouvelle branche appelée old dans le référentiel actuel:

    git checkout -b old old-git/master`
    
  6. Rebase pour mettre le HEAD au-dessus de old-git / master. Cela maintiendra tous vos engagements. Ce que cela signifie, c'est de prendre tout votre travail effectué dans Git et de le mettre au-dessus du travail auquel vous accédez à partir de SVN.

    git rebase master
    
  7. Revenez maintenant à votre branche principale:

    git checkout master
    

    Et vous pouvez voir que vous avez un historique de validation propre. C'est ce que vous voulez pousser vers SVN.

  8. Poussez votre travail vers SVN:

    git svn dcommit
    

C'est tout. Il est très propre, pas de piratage, et tout fonctionne parfaitement hors de la boîte. Prendre plaisir.


Processus similaire également détaillé sur chani.wordpress.com/2012/01/25/…
TWiStErRob

Ressemble à. Cependant, je trouve son chemin assez déroutant et le mien est beaucoup plus court (8 étapes que 19/23). Peut-être que je ne la comprends pas correctement, mais je pense qu'elle mélange les commandes svn et git à sa deuxième puce.
codingdave

Ah, oui, la différence est que votre processus importe git à la racine du dépôt SVN, le sien l'importe dans un sous-dossier, c'est pourquoi les quelques git svnpréparations sont nécessaires.
TWiStErRob

À l'étape 7, ne devrait-il pas y avoir de fusion de git old? Pour moi il semble que vous fassiez un dcommit de master que vous n'avez pas changé?!
Alexander

@Alexander utilisant 'git rebase master', nous générons une fusion rapide, c'est-à-dire une fusion linéaire sans validation de fusion ayant deux parents. Nous voulons avoir une histoire linéaire ici.
codingdave


3

J'avais besoin de valider mon référentiel Git existant dans un référentiel SVN vide.

Voici comment j'ai réussi à faire ceci:

$ git checkout master
$ git branch svn
$ git svn init -s --prefix=svn/ --username <user> https://path.to.repo.com/svn/project/
$ git checkout svn
$ git svn fetch
$ git reset --hard remotes/svn/trunk
$ git merge master
$ git svn dcommit

Cela a fonctionné sans problème. J'espère que ça aidera quelqu'un.

Comme je devais m'autoriser avec un nom d'utilisateur différent du référentiel SVN (mon originutilise l'authentification par clé privée / publique), j'ai dû utiliser la --usernamepropriété.


vous devrez peut-être installer git-svnavant que cela ne soit possible, voir stackoverflow.com/questions/527037/git-svn-not-a-git-command
flexponsive

2

Si vous souhaitez continuer à travailler avec Git en tant que référentiel principal et que vous avez juste besoin d'exporter les révisions vers SVN de temps en temps, vous pouvez utiliser Tailor pour garder le référentiel SVN synchronisé. Il peut copier des révisions entre différents systèmes de contrôle de source et mettrait à jour le SVN avec les modifications que vous apportez dans Git.

Je n'ai pas essayé de conversion de Git en SVN, mais pour un exemple SVN -> SVN, voyez cette réponse .


2

Encore une autre séquence qui a fonctionné (avec quelques commentaires sur chaque étape):

  1. Installation git-svnet subversionkits d'outils:

    sudo apt-get install git-svn subversion
    
  2. Basculez à l'intérieur du PROJECT_FOLDER

    cd PROJECT_FOLDER
    
  3. Créez le chemin du projet sur le serveur Subversion (malheureusement le git-svnplugin actuel a un défaut par rapport à TortoiseSVN). Il ne peut pas stocker le code source directement dans le PROJECT_FOLDER. Au lieu de cela, par défaut, il téléchargera tout le code dans PROJECT_FOLDER/trunk.

    svn mkdir --parents protocol: /// path / to / repo / PROJECT_FOLDER / trunk -m "create git repo placeholder"

C'est l'endroit où trunkà la fin du chemin est obligatoire

  1. Initialiser le git-svncontexte du plugin dans le .gitdossier

    git svn init -s protocol:///path/to/repo/PROJECT_FOLDER
    

    C'est l'endroit où trunkà la fin du chemin est inutile

  2. Récupérer une Subversioninformation de référentiel vide

    git svn fetch
    

    Cette étape aide à synchroniser le serveur Subversion avec le git-svnplugin. C'est le moment où le git-svnplugin établit le remotes/originchemin et l'associe au trunksous - dossier côté serveur.

  3. Rebase d'anciens commits Git se sont produits avant que le git-svnplugin ne soit impliqué dans le processus (cette étape est facultative )

    git rebase origin/trunk
    
  4. Ajouter des fichiers nouveaux / modifiés à valider (cette étape est régulière pour les activités Git et est facultative )

    git add .
    
  5. Validez les fichiers récemment ajoutés dans le référentiel Git local (cette étape est facultative et n'est applicable que si l'étape 7 a été utilisée):

    git commit -m "Importing Git repository"
    
  6. Pousser tout l'historique des modifications du projet dans le serveur Subversion:

    git svn dcommit
    

1

Vous pouvez créer un nouveau référentiel SVN. Exportez votre projet Git (étoffez les fichiers .git). Ajoutez-le au référentiel SVN (initialisation du référentiel avec ce que vous aviez jusqu'à présent dans Git). Utilisez ensuite les instructions pour importer des référentiels SVN dans un nouveau projet Git.

Mais cela perdra votre historique Git précédent.



1

Je voudrais partager un excellent outil utilisé dans la communauté WordPress appelé Scatter

Plugins Git WordPress et un peu de santé mentale

Cela permet aux utilisateurs d'envoyer automatiquement leur référentiel Git à wordpress.org SVN. En théorie, ce code peut être appliqué à n'importe quel référentiel SVN.


Le lien semble effectivement rompu ( "Le serveur evansolomon.me met trop de temps à répondre." ).
Peter Mortensen

1

J'ai récemment dû migrer plusieurs référentiels Git vers SVN, et après avoir essayé toutes les solutions que j'ai pu trouver, ce qui a finalement fonctionné pour moi a été Mercurial (oui, en utilisant un troisième VCS). En utilisant ce guide , j'ai trouvé le processus suivant (sous Linux, mais l'idée de base devrait également fonctionner sous Windows).

  1. Les packages nécessaires:

    $ sudo apt-get install git subversion mercurial python-subversion
    
  2. Mercurial doit être configuré en ajoutant ce qui suit à ~/.hgrc :

    [extensions]
    hgext.convert=
    
  3. Créez des répertoires de travail temporaires (j'avais plusieurs référentiels à migrer, j'ai donc créé des répertoires pour les versions SVN et Git, pour les garder séparés):

    $ mkdir svn
    $ mkdir git
    
  4. Créez un référentiel SVN local vide:

    $ svnadmin create svn/project
    
  5. Clonez le référentiel Git existant:

    $ git clone server/path/project.git git/project
    
  6. Laissez Mercurial faire son travail:

    $ hg convert --dest-type svn git/project svn/project
    
  7. Maintenant, le référentiel SVN devrait contenir l'historique complet des validations, mais pas avec les horodatages d'origine. Si ce n'est pas un problème, passez à l'étape suivante à l'étape 11.

  8. Avec un peu de travail, la date et l'heure de chaque commit peuvent être modifiées . Étant donné que mes référentiels sont assez petits, il était possible pour moi de le faire manuellement. Tout d'abord, créez un pre-revprop-changehook dans le référentiel SVN avec le contenu suivant, pour permettre la modification de la propriété nécessaire:

    #!/bin/bash
    exit 0;
    

    Ce script doit être rendu exécutable:

    $ chmod +x svn/project/hooks/pre-revprop-change
    
  9. Mercurial a créé une copie de travail du référentiel SVN, nommée project -wc, alors passez-y et modifiez les temps de commit:

    $ cd project-wc
    $ svn propedit svn:date --revprop -r 1
    

    Entrez la date et l'heure correctes (faites attention aux fuseaux horaires!) Et enregistrez. Vous devriez recevoir un message disant "Définir une nouvelle valeur pour la propriété svn: date lors de la révision 1".
    Maintenant, rincez et répétez pour chaque autre révision.

  10. Vérifiez éventuellement l'historique des validations pour vous assurer que tout semble correct:

    $ svn log -r 1:HEAD
    

    Remontez ensuite d'un niveau:

    $ cd ..
    
  11. Vider le référentiel:

    $ svnadmin dump svn/project > project.dump
    
  12. Et chargez le vidage sur votre serveur Subversion. Terminé!

Ce processus fonctionnerait probablement aussi directement entre les référentiels distants, mais j'ai trouvé plus facile de travailler avec des référentiels locaux. Fixer les temps de validation était beaucoup de travail, mais dans l'ensemble, le processus était beaucoup plus simple que toute autre méthode que j'ai trouvée.


1

il existe trois méthodes:

  1. rebaser: comme les autres réponses

  2. commit id: trouver svn first commit id et git first commit id, les reproduire dans .git / info / grafts: echo "git_id svn_id}" > .git/info/graftsthengit svn dcommit

  3. extraire chaque commit git, copier les fichiers dans svn_repo, svn commit

démo bash: démo github

v1.x : utiliser rebase et commit id

v2.x: utiliser des fichiers de copie, puis svn commit


0

Dans mon cas, j'ai dû lancer un projet propre depuis SVN

$ Project> git svn init protocol://path/to/repo -s
$ Project> git svn fetch

ajoutez toutes vos sources de projet ...

$ Project> git add .
$ Project> git commit -m "Importing project sources"
$ Project> git svn dcommit

0

Je veux juste partager une partie de mon expérience avec la réponse acceptée. J'ai fait toutes les étapes et tout allait bien avant de lancer la dernière étape:

git svn dcommit

$ git svn dcommit

Utilisation de la valeur non initialisée $ u en substitution (s ///) à /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm ligne 101.

Utilisation de la valeur non initialisée $ u dans la concaténation (.) Ou de la chaîne à /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm ligne 101. refs / remotes / origin / HEAD: ' https://192.168.2.101/ svn / PROJECT_NAME 'introuvable dans' '

J'ai trouvé le fil https://github.com/nirvdrum/svn2git/issues/50 et enfin la solution que j'ai appliquée dans le fichier suivant à la ligne 101 /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm

j'ai remplacé

$u =~ s!^\Q$url\E(/|$)!! or die

avec

if (!$u) {
    $u = $pathname;
} 
else {
       $u =~ s!^\Q$url\E(/|$)!! or die
      "$refname: '$url' not found in '$u'\n";
}

Cela a résolu mon problème.


-1

Et si vous ne voulez pas engager tous les commit que vous faites dans Git, dans le dépôt SVN? Que se passe-t-il si vous souhaitez simplement envoyer des validations de manière sélective? Eh bien, j'ai une meilleure solution.

Je garde un référentiel Git local où tout ce que je fais est de récupérer et de fusionner depuis SVN. De cette façon, je peux m'assurer que j'inclus toutes les mêmes modifications que SVN, mais je garde mon historique de commit complètement séparé du SVN.

Ensuite, je garde une copie de travail locale SVN distincte qui se trouve dans un dossier séparé. C'est celui à partir duquel je valide à nouveau SVN, et j'utilise simplement l'utilitaire de ligne de commande SVN pour cela.

Lorsque je suis prêt à valider l'état de mon référentiel Git local sur SVN, je copie simplement tout le désordre des fichiers dans la copie de travail SVN locale et je le valide à partir de là en utilisant SVN plutôt que Git.

De cette façon, je n'ai jamais à effectuer de rebasage, car le rebasage est comme la base libre.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.