Empujar un repositorio Git existente a SVN

385

He estado haciendo todo mi trabajo en Git y presionando a GitHub. He estado muy contento con el software y el sitio, y no deseo cambiar mis prácticas de trabajo en este momento.

Mi asesor de doctorado está pidiendo a todos los estudiantes que mantengan su trabajo en un repositorio SVN alojado en la universidad. He encontrado toneladas de documentación y tutoriales a punto de extraer un repositorio SVN existente en Git, pero nada sobre empujar un repositorio Git a un nuevo repositorio SVN. Supongo que debe haber alguna forma de hacerlo con una combinación de git-svn y una nueva rama y rebase y todos esos términos maravillosos, pero soy un novato de Git y no me siento seguro con ninguno de ellos.

Luego, solo quiero ejecutar un par de comandos para enviar confirmaciones a ese repositorio SVN cuando elijo. Deseo seguir usando Git y solo hacer que el repositorio SVN refleje lo que hay en Git.

Seré la única persona que se comprometa con SVN, si esto hace alguna diferencia.

cflewis
fuente
1
Nota: probablemente perderá sus sellos de fecha originales cuando haga esto. Las nuevas fechas se basarán en el momento de la importación a Subversion.
nobar
Para aquellos que buscan información más actualizada, algunos pueden encontrar útil la siguiente publicación durante su búsqueda de automatización: deliciousbrains.com/deploying-wordpress-plugins-travis
Josh Habdas

Respuestas:

403

También necesitaba esto, y con la ayuda de la respuesta de Bombe + algunos retoques, lo hice funcionar. Aquí está la receta:

Importar 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

Después del n. ° 3, recibirá un mensaje críptico como este:

Usando un mayor nivel de URL: protocol:///path/to/repo/PROJECT => protocol:///path/to/repo

Solo ignora eso.

Cuando se ejecuta # 5, se puede conseguir conflictos. Resuélvalos agregando archivos con el estado "no combinado" y reanudando el rebase. Eventualmente, habrás terminado; luego sincronice nuevamente al repositorio SVN, usando dcommit. Eso es todo.

Mantener los repositorios sincronizados

Ahora puede sincronizar desde SVN a Git, utilizando los siguientes comandos:

git svn fetch
git rebase trunk

Y para sincronizar de Git a SVN, use:

git svn dcommit

Nota final

Es posible que desee probar esto en una copia local, antes de aplicar a un repositorio en vivo. Puede hacer una copia de su repositorio Git en un lugar temporal; simplemente use cp -r, ya que todos los datos están en el repositorio mismo. Luego puede configurar un repositorio de pruebas basado en archivos, utilizando:

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

Y verifique una copia de trabajo usando:

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

Eso te permitirá jugar con las cosas antes de hacer cambios duraderos.

Anexo: Si te equivocas git svn init

Si accidentalmente ejecuta git svn initcon la URL incorrecta, y no fue lo suficientemente inteligente como para hacer una copia de seguridad de su trabajo (no pregunte ...), no puede ejecutar el mismo comando nuevamente. Sin embargo, puede deshacer los cambios emitiendo:

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

Y eliminar la sección [svn-remote "svn"]sección.

Entonces puedes correr de git svn initnuevo.

troelskn
fuente
2
Buena respuesta. ¿Esto también arruina las fechas de confirmación?
Drew Noakes
44
Buenas preguntas: lamentablemente, tampoco sé la respuesta. Esta es más una guía práctica de lo que encontré que funciona. No entiendo completamente todos los detalles. En cuanto a las fechas de confirmación, supongo que podría hacer una prueba y averiguarlo. Recuerde que puede iniciar un repositorio svn local (basado en fs) para probar cosas.
troelskn
10
He seguido estos mismos pasos usando "git rebase --onto trunk --root" en lugar del paso 5 y tuve mucho más éxito. Solo un puñado de conflictos de fusión para resolver, en lugar de toneladas.
kubi
55
En mi caso, esta secuencia no funcionó. Siempre se mostraba el mensaje "No se puede determinar la información SVN ascendente del historial HEAD". Por lo tanto, no es posible comprometerse.
Fedir RYKHTIK
2
No importa, traté de ser inteligente y omití el -s ya que no quería seguir la configuración estándar de SVN (trunk / branch / tags /). No podría hacer que funcione sin eso.
James McMahon el
33

Así es como lo hicimos funcionar:

Clone su repositorio Git en algún lugar de su máquina.

Abra .git / config y agregue lo siguiente (desde Mantener un espejo SVN de solo lectura de un repositorio Git ):

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

Ahora, desde una ventana de consola, escriba estos:

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

Ahora, si se rompe aquí por cualquier razón, escriba estas tres líneas:

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

Y finalmente, puedes comprometerte con SVN:

git svn dcommit

Nota: Siempre descarto esa carpeta después.

Alex Rouillard
fuente
66
+1 esto realmente funcionó para mí (no tengo tronco / base lo que sea), en contraste con otras respuestas que seguían dandoUnable to determine upstream SVN information from HEAD history.
stijn
2
Probé esta técnica, pero no importó el historial. Por cierto, "git merge master" es ahora "git merge --allow-non-related-historories master"
Berend de Boer el
1
Si desea sobrescribir absolutamente lo que está en SVN con lo que está en git, use git merge -s recursive -Xtheirs --allow-unrelated-histories master.
user1475814
28

Usar git rebasedirectamente perderá la primera confirmación. Git lo trata de manera diferente y no lo puede refrenar.

Hay un procedimiento que preservará la historia completa: http://kerneltrap.org/mailarchive/git/2008/10/26/3815034

Transcribiré la solución aquí, pero los créditos son para Björn.

Inicialice git-svn:

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

El --prefix le da ramas de seguimiento remoto como "svn / trunk", lo cual es bueno porque no obtiene nombres ambiguos si llama a su rama local solo "trunk". Y -ses un acceso directo para el diseño estándar de troncales / etiquetas / ramas.

Obtener las cosas iniciales de SVN:

git svn fetch

Ahora busque el hash de su confirmación raíz (debe mostrar una confirmación única):

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

Luego obtenga el hash del commit de tronco vacío:

git rev-parse svn/trunk

Crea el injerto:

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

Ahora, "gitk" debería aparecer svn/trunkcomo la primera confirmación en la que se basa su rama maestra.

Hacer el injerto permanente:

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

Suelta el injerto:

rm .git/info/grafts

gitk aún debería aparecer svn/trunken la ascendencia del maestro.

Linealice su historial en la parte superior del tronco:

git svn rebase

Y ahora "git svn dcommit -n" debería decirle que se va a comprometer con trunk.

git svn dcommit
Peter Mortensen
fuente
¿Puede explicar cómo esta técnica es diferente de la anterior más claramente?
cmcginty
3
Cuando intento "git rev-parse svn / trunk", informa una revisión desconocida o una ruta que no está en el árbol de trabajo.
Adam Ness
Esta es la única respuesta que funcionó para mí, excepto que los pasos git filter-branch y drop graft no fueron necesarios: realicé un rebase después de crear el injerto, y luego hice git svn dcommit.
fc7
8

Cree un nuevo directorio en el repositorio de Subversion para su proyecto.

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

Cambie a su proyecto administrado por Git e inicialice git-svn.

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

Esto creará una única confirmación porque el directorio de su proyecto SVN todavía está vacío. Ahora vuelva a basar todo en ese compromiso, git svn dcommity ya debería haber terminado. Sin embargo, arruinará seriamente tus fechas de confirmación.

Bombe
fuente
Usé esta respuesta con las instrucciones en hassox.blogspot.com/2007/12/using-git-with-svn.html Seguí estos comandos, luego "#git branch -a" para ver el nombre del tronco. Luego: # git checkout -b local-svn trunk # git merge master # git svn dcommit ¡Recuerde .gitignore el directorio .svn!
cflewis
Como acabo de hacer la misma operación, quería hacer explícito que desde hace algún tiempo (enero '09), git puede realizar una operación de rebase en el commit raíz. Esto hace que el proceso sea mucho más simple de lo que indican muchos de los artículos anteriores, vea los comentarios en its.arubything.com/2009/1/4/…
Louis Jacomet
¿Qué hace la opción "-s" git svn init? No puedo ver esto en las páginas de manual de git svn.
Nathan
Lea la página de manual nuevamente, quizás busque “-s” porque está allí. Es un alias para "--stdlayout".
Bombe
7

Git -> SVN con historial de confirmación completo

Tenía un proyecto Git y tuve que moverlo a SVN. Así es como lo hice, manteniendo todo el historial de confirmaciones. Lo único que se pierde es el tiempo de confirmación original, ya que libSVN establecerá la hora local cuando lo hagamos git svn dcommit.

Cómo:

  1. Tener un repositorio SVN donde queremos importar nuestras cosas y clonarlo con git-svn:

    git svn clone https://path.to/svn/repository repo.git-svn`
    
  2. Ve allí:

    cd repo.git-svn
    
  3. Agregue el control remoto del repositorio Git (en este ejemplo estoy usando C: /Projects/repo.git ). Desea pasar a SVN y darle el nombre old-git:

    git remote add old-git file:///C/Projects/repo.git/
    
  4. Obtenga la información de la rama maestra desde el repositorio de git antiguo al repositorio actual:

    git fetch old-git master
    
  5. Verifique la rama maestra del control remoto old-git en una nueva rama llamada old en el repositorio actual:

    git checkout -b old old-git/master`
    
  6. Rebase para colocar el HEAD encima de old-git / master. Esto mantendrá todos tus commits. Lo que esto hace básicamente es tomar todo su trabajo realizado en Git y ponerlo encima del trabajo al que accede desde SVN.

    git rebase master
    
  7. Ahora regrese a su rama maestra:

    git checkout master
    

    Y puede ver que tiene un historial de confirmación limpio. Esto es lo que quieres enviar a SVN.

  8. Empuje su trabajo a SVN:

    git svn dcommit
    

Eso es todo. Está muy limpio, no se piratea, y todo funciona perfectamente fuera de la caja. Disfrutar.

codingdave
fuente
Proceso similar también detallado en chani.wordpress.com/2012/01/25/…
TWiStErRob
Parece. Sin embargo, encuentro su camino bastante confuso y el mío es mucho más corto (8 pasos que 19/23). Tal vez no la estoy entendiendo correctamente, pero creo que ella mezcla los comandos svn y git en su segunda bala.
codingdave
Ah, sí, la diferencia es que su proceso importa git a la raíz del repositorio SVN, el suyo lo importa a una subcarpeta, es por eso que git svnse necesitan pocas preparaciones.
TWiStErRob
En el paso 7, ¿no debería haber un git merge old? ¡Para mí parece que estás haciendo un dcommit de master que no has cambiado?
Alexander
@Alexander usando 'git rebase master' producimos una combinación de avance rápido, que es una combinación lineal sin una confirmación de fusión que tiene dos padres. Queremos tener una historia lineal aquí.
codingdave
4

Propondría una instrucción muy corta en 4 comandos usando SubGit . Ver esta publicación para más detalles.

Dmitry Pavlenko
fuente
3

Necesitaba confirmar mi repositorio Git existente en un repositorio SVN vacío.

Así es como logré hacer esto:

$ 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

Funcionó sin problemas. Espero que esto ayude a alguien.

Como tenía que autorizarme con un nombre de usuario diferente al repositorio SVN (mi originusa autenticación de clave privada / pública), tuve que usar la --usernamepropiedad.

Jay Linski
fuente
es posible que deba instalar git-svnantes de que esto sea posible, consulte stackoverflow.com/questions/527037/git-svn-not-a-git-command
flexponsive
2

Si desea seguir trabajando con Git como su repositorio principal y solo necesita "exportar" las revisiones a SVN de vez en cuando, puede usar Tailor para mantener el repositorio SVN sincronizado. Puede copiar revisiones entre diferentes sistemas de control de fuente y actualizaría el SVN con los cambios que realice en Git.

No he intentado una conversión de Git a SVN, pero para un ejemplo SVN -> SVN, vea esta respuesta .

algo
fuente
2

Otra secuencia que funcionó (con algunos comentarios en cada paso):

  1. Instalar git-svny subversionkits de herramientas:

    sudo apt-get install git-svn subversion
    
  2. Cambiar dentro del PROJECT_FOLDER

    cd PROJECT_FOLDER
    
  3. Cree la ruta del proyecto en el servidor de Subversion (desafortunadamente el git-svncomplemento actual tiene un defecto en comparación con TortoiseSVN). No puede almacenar el código fuente directamente en el PROJECT_FOLDER. En cambio, de forma predeterminada, cargará todo el código PROJECT_FOLDER/trunk.

    svn mkdir - protocolo de los padres: /// ruta / a / repo / PROJECT_FOLDER / trunk -m "crear marcador de posición de repositorio git"

Este es el lugar donde trunkal final del camino es obligatorio

  1. Inicialice el git-svncontexto del complemento dentro de la .gitcarpeta

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

    Este es el lugar donde trunkal final del camino es innecesario

  2. Obtener una Subversioninformación de repositorio vacía

    git svn fetch
    

    Este paso está ayudando a sincronizar el servidor Subversion con el git-svncomplemento. Este es el momento en que el git-svncomplemento establece la remotes/originruta y la asocia con la trunksubcarpeta en el lado del servidor.

  3. Rebase las confirmaciones de Git anteriores antes de que el git-svncomplemento se involucrara en el proceso (este paso es opcional )

    git rebase origin/trunk
    
  4. Agregue archivos nuevos / modificados para confirmar (este paso es regular para las actividades de Git y es opcional )

    git add .
    
  5. Confirme los archivos recién agregados en el repositorio local de Git (este paso es opcional y solo es aplicable si se ha utilizado el paso 7):

    git commit -m "Importing Git repository"
    
  6. Empujar todo el proyecto cambia el historial en el servidor de Subversion:

    git svn dcommit
    
Oleg Kokorin
fuente
1

Puede hacer un nuevo repositorio SVN. Exporte su proyecto Git (desarrollando los archivos .git). Agréguelo al repositorio SVN (inicializando el repositorio con lo que tenía hasta ahora en Git). Luego, use las instrucciones para importar repositorios SVN en un proyecto Git nuevo.

Pero esto perderá tu historial anterior de Git.

Vasil
fuente
1

Si no tiene que usar ningún SVN específico y está usando GitHub, puede usar su conector SVN.

Más información está aquí: Colaborando en GitHub con Subversion

elmariofredo
fuente
SVN qué? En ( "... cualquier SVN específico y ..." ). Versión SVN? Cliente SVN? ¿O algo mas?
Peter Mortensen
1

Me gustaría compartir una gran herramienta que se utiliza en la comunidad de WordPress llamada Scatter

Complementos de Git WordPress y un poco de dispersión de cordura

Esto permite a los usuarios poder enviar su repositorio Git a wordpress.org SVN automáticamente. En teoría, este código se puede aplicar a cualquier repositorio SVN.

r109
fuente
El enlace parece estar efectivamente roto ( "El servidor en evansolomon.me está tardando demasiado en responder" ).
Peter Mortensen
1

Recientemente tuve que migrar varios repositorios de Git a SVN, y después de probar todas las soluciones que pude encontrar, lo que finalmente funcionó para mí fue Mercurial (sí, usando un tercer VCS). Usando esta guía , se me ocurrió el siguiente proceso (en Linux, pero la idea básica también debería funcionar en Windows).

  1. Los paquetes necesarios:

    $ sudo apt-get install git subversion mercurial python-subversion
    
  2. Mercurial debe configurarse agregando lo siguiente a ~/.hgrc:

    [extensions]
    hgext.convert=
    
  3. Cree algunos directorios de trabajo temporales (tenía que migrar varios repositorios, así que creé directorios para las versiones SVN y Git, para mantenerlos separados):

    $ mkdir svn
    $ mkdir git
    
  4. Haga un repositorio SVN local vacío:

    $ svnadmin create svn/project
    
  5. Clonar el repositorio Git existente:

    $ git clone server/path/project.git git/project
    
  6. Deje que Mercurial haga lo suyo:

    $ hg convert --dest-type svn git/project svn/project
    
  7. Ahora el repositorio SVN debería contener el historial de confirmación completo, pero no con las marcas de tiempo originales. Si esto no es un problema, omita la siguiente parte del paso 11.

  8. Con un poco de trabajo, la fecha y la hora de cada confirmación se pueden cambiar . Dado que mis repositorios son bastante pequeños, me fue posible hacerlo manualmente. Primero, cree un pre-revprop-changeenlace en el repositorio SVN con los siguientes contenidos, para permitir que se modifique la propiedad necesaria:

    #!/bin/bash
    exit 0;
    

    Este script tiene que hacerse ejecutable:

    $ chmod +x svn/project/hooks/pre-revprop-change
    
  9. Mercurial creó una copia de trabajo del repositorio SVN, llamado project -wc, así que cambie a él y edite los tiempos de confirmación:

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

    Ingrese la fecha y hora correctas (¡preste atención a las zonas horarias!) Y guarde. Debería recibir un mensaje que dice "Establecer un nuevo valor para la propiedad svn: fecha de revisión 1".
    Ahora enjuague y repita para cada otra revisión.

  10. Opcionalmente, verifique el historial de confirmación para asegurarse de que todo se vea bien:

    $ svn log -r 1:HEAD
    

    Luego vuelve a subir un nivel:

    $ cd ..
    
  11. Volcar el repositorio:

    $ svnadmin dump svn/project > project.dump
    
  12. Y cargue el volcado en su servidor Subversion. ¡Hecho!

Este proceso probablemente también funcionaría directamente entre repositorios remotos, pero me resultó más fácil trabajar con los locales. Arreglar los tiempos de confirmación fue mucho trabajo, pero en general el proceso fue mucho más directo que cualquier otro método que encontré.

Indrek
fuente
1

Hay tres métodos:

  1. rebase : como las otras respuestas

  2. commit id: encuentre svn first commit id y git first commit id, haga eco de ellos en .git / info / grafts: echo "git_id svn_id}" > .git/info/graftsluegogit svn dcommit

  3. compruebe cada confirmación de git, copie archivos en svn_repo, svn commit

demostración de bash: demostración de github

v1.x: use rebase y commit id

v2.x: use archivos de copia, luego svn commit

张耀文
fuente
0

En mi caso, tuve que iniciar un proyecto limpio desde SVN

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

agregue todas las fuentes de su proyecto ...

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

Solo quiero compartir algo de mi experiencia con la respuesta aceptada. Hice todos los pasos y todo estuvo bien antes de ejecutar el último paso:

git svn dcommit

$ git svn dcommit

Uso del valor no inicializado $ u en sustitución (s ///) en /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm línea 101.

Uso del valor no inicializado $ u en concatenación (.) O cadena en /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm línea 101. refs / remotes / origin / HEAD: ' https://192.168.2.101/ svn / PROJECT_NAME 'no encontrado en' '

Encontré el hilo https://github.com/nirvdrum/svn2git/issues/50 y finalmente la solución que apliqué en el siguiente archivo en la línea 101 /usr/lib/perl5/vendor_perl/5.22/Git/SVN.pm

Reemplacé

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

con

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

Esto solucionó mi problema.

Pavel Slepiankou
fuente
-1

¿Qué sucede si no desea confirmar cada confirmación que realiza en Git, en el repositorio SVN? ¿Qué sucede si solo desea enviar confirmaciones selectivamente? Bueno, tengo una mejor solución.

Mantengo un repositorio local de Git donde todo lo que hago es buscar y fusionar desde SVN. De esa manera puedo asegurarme de incluir todos los mismos cambios que SVN, pero mantengo mi historial de confirmaciones completamente separado del SVN.

Luego mantengo una copia de trabajo local SVN separada que está en una carpeta separada. Ese es el que hago confirmaciones a SVN, y simplemente uso la utilidad de línea de comando SVN para eso.

Cuando estoy listo para confirmar el estado de mi repositorio local de Git en SVN, simplemente copie todo el desorden de archivos en la copia de trabajo local de SVN y lo confirmo desde allí usando SVN en lugar de Git.

De esta manera, nunca tengo que hacer un rebase, porque el rebase es como una base libre.

Tostada Coma
fuente