Lihuen
RSSRSS AtomAtom

Diferencia entre revisiones de «Migración de SVN a Git»

(Branches y Tags en Git)
(Migración de repositorios con el esquema (c))
Línea 51: Línea 51:
 
==Migración de repositorios con el esquema (c)==
 
==Migración de repositorios con el esquema (c)==
 
Si bien el esquema (c) es el más desprolijo y no es para nada recomendable, es el más fácil de migrar  a git:
 
Si bien el esquema (c) es el más desprolijo y no es para nada recomendable, es el más fácil de migrar  a git:
 +
git svn clone https://svn.servidor.com/svn/lihuen/script-utils
 +
 +
Podemos inspeccionar la historia del proyecto y ver que todo está en orden con:
 +
git log
 +
 +
Luego agregamos el servidor remoto Git donde queremos subir el proyecto con toda su historia:
 +
git remote add origin git@git.servidor.com:usuario/script-utils.git
 +
 +
Y finalmente hacemos un push del branch master (guardándolo de ahora en más como branch por defecto para los push y pull):
 +
git push -u origin master
 +
 +
==Migración de repositorios con esquemas (a) y (b)==
 +
En estos esquemas podemos aprovechar la división de tags y branches y exportar los mismos a Git. Hacer esto manualmente demanda bastante trabajo, así que podemos automatizar todo con un script:
 +
 +
<code language="bash">
 +
#!/bin/sh
 +
set -e
 +
git(){
 +
    (tput bold; tput setf 2; echo git "$@"; tput sgr0) 1>&2
 +
$(which git) "$@"
 +
}
 +
 +
ask(){
 +
tput bold; tput setf 4
 +
read -p "$1 [S/n]: " answer
 +
tput sgr0
 +
case "$answer" in
 +
N|n)
 +
return 1
 +
;;
 +
S|s|"")
 +
return 0
 +
;;
 +
*)
 +
echo "Respuesta no válida"
 +
ask "$1"
 +
return $?
 +
esac
 +
return 0
 +
}
 +
 +
 +
fetch(){
 +
# http://stackoverflow.com/questions/2244252/importing-svn-branches-and-tags-on-git-svn
 +
# supply proper trunk, braches and tags folder names and fetch svn repo:
 +
git svn init -t tags -b branches -T trunk "https://svn.servidor.com/svn/lihuen/$1"
 +
git svn fetch
 +
 +
# Since tags in svn are real braches, create git tags from tag branches:
 +
git for-each-ref '--format=%(refname:short) %(objectname)' refs/remotes/tags |  cut -d / -f 2- |
 +
while read ref
 +
do
 +
  [ "$ref" = "remotes/tags" ] && continue # Descarto esta ref
 +
  git tag -a svn_$ref -m 'import tag from svn'
 +
done
 +
 +
# Delete tag branches
 +
 +
git for-each-ref '--format=%(refname:short)' refs/remotes/tags | cut -d / -f 2- |
 +
while read ref
 +
do
 +
  [ "$ref" = "remotes/tags" ] && continue # Descarto esta ref
 +
  git branch -rd tags/$ref
 +
done
 +
# Since tags marked in the previous step point to a commit "create tag", we need to derive "real" tags, i.e. parents of "create tag" commits.
 +
 +
git for-each-ref '--format=%(refname:short)' refs/tags |
 +
while read ref
 +
do
 +
  tag=`echo $ref | sed -r 's/^svn_//g'` # give tags a new name
 +
  echo $ref -\> $tag
 +
  git tag -a $tag `git rev-list -2 $ref | tail -1` -m "proper svn tag"
 +
  git tag -d svn_$tag # Elimino el tag temporal
 +
done
 +
 +
# Convierto los branches remotos en branches locales
 +
git for-each-ref refs/remotes | grep -v '@' |grep -v 'remotes/trunk$' | cut -d/ -f3- |
 +
while read ref; do
 +
git branch "$ref" "remotes/$ref"
 +
done
 +
}
 +
 +
push(){
 +
 +
echo -- Tags
 +
git tag -l
 +
echo -- Branches
 +
git branch -v
 +
 +
ask "Si la información anterior es correcta ingresá S para seguir" || exit 0
 +
 +
while true; do
 +
read -p "Ingresá el namespace del proyecto (por ej. tu nombre de usuario): " usuario
 +
ask "¿El namespace a usar es {$usuario}?" || continue
 +
break
 +
done
 +
echo "Crea en gitlab (desde la web) un repositorio con el nombre $usuario/$1, después presioná [Enter] para seguir"
 +
read junk
 +
 +
git remote add origin "git@git.servidor.com:$usuario/$1.git"
 +
git push origin --mirror
 +
}
 +
 +
 +
if [ $# -ne 1 ]; then
 +
echo "Falta el nombre del repo svn"
 +
echo "Por ejemplo: $0 lihuenconfig"
 +
exit 1
 +
fi
 +
if [ -n "$(echo $1 | grep -E '/$')" ]; then
 +
echo "El nombre del repositorio no puede terminar en /"
 +
exit 2
 +
fi
 +
 +
ORIGINAL="$PWD"
 +
FETCH=1
 +
if [ -d "$1" ]; then
 +
ask "El directorio $1 ya existe ¿subo lo que está ahí a gitlab?" || exit 1
 +
FETCH=0
 +
fi
 +
 +
 +
 +
if [ $FETCH -eq 1 ]; then
 +
mkdir "$1"
 +
cd "$1"
 +
fetch "$1" # Descarga el repo svn usando git-svn y acomoda los tags y branches
 +
ask "Importado a repo GIT ¿Querés seguir en el asistente para subirlo al gitlab?" || exit 0
 +
fi
 +
[ $FETCH -eq 0 ] && cd "$1"
 +
push "$1" # Agrega un repo git como remote y sube todo al mismo (con --mirror)
 +
 +
cd $ORIGINAL
 +
</code>

Revisión de 16:09 26 sep 2013

Existe un paquete, git-svn, que agrega al comando Git, la capacidad de trabajar con repositorios remotos SVN, dependiendo de la estructura que tenga el repositorio SVN a migrar, se puede usar este comando directamente o desde un script que simplifique la tarea.

Estructuras de directorios en SVN

Los repositorios SVN se estructuran en directorios y hay distintos esquemas para estructurarlos, el libro "Control de versiones con Subversion" recomienda 2 posibles esquemas [1].

Estos esquemas reservan un directorio para:

  • trunk -> versión principal de desarrollo.
  • branches -> versiones que divergen de trunk (por ejemplo en un branch dado se agrega una funcionalidad experimental).
  • tags -> versiones con nombre del proyecto (por ejemplo 0.9beta, 0.9, 1.0, etc...).

Los esquemas (a) y (b) son los recomendados por el libro: a. Para repositorios que contienen un único proyecto:

/
   trunk/
   branches/
   tags/

b. Para repositorios con múltiples proyectos:

/
   paint/
      trunk/
      branches/
      tags/
   calc/
      trunk/
      branches/
      tags/

Pero como no hay ninguna restricción técnica para hacerlo de otra forma, uno podría organizar su repositorio como quisiera. Por ejemplo poniendo todos los archivos del proyectos sueltos:

c. Forma desprolija con los archivos sueltos:

/
  main.c
  Makefile
  funcs.c
  funcs.h

Sin embargo en este ultimo formato es imposible tener branches y tags.

Branches y Tags en Git

Git conceptualmente es similar, existen branches (ramificaciones) donde se almacenarán modificaciones que hagan que el proyecto diverja respecto de la versión de referencia. Y tags que marcan versiones con nombre del proyecto. Sin embargo la implementación es muy distinta.

En Git el código no está organizado en directorios como en los esquemas (a) y (b) de SVN antes mencionados, sino que para el usuario se ven como (c). Para acceder a un branch determinado o a un tag se usa checkout:

git checkout nombre_tag_o_branch

Checkout altera, crea y borra los archivos que sea necesario para que el usuario vea el branch o tag (o commit) seleccionado. Para saber en que branch se está actualmente se puede usar el comando:

git status

Por convención el branch master es la línea principal de desarrollo (como el trunk en SVN).

Migración de repositorios con el esquema (c)

Si bien el esquema (c) es el más desprolijo y no es para nada recomendable, es el más fácil de migrar a git:

git svn clone https://svn.servidor.com/svn/lihuen/script-utils

Podemos inspeccionar la historia del proyecto y ver que todo está en orden con:

git log

Luego agregamos el servidor remoto Git donde queremos subir el proyecto con toda su historia:

git remote add origin git@git.servidor.com:usuario/script-utils.git

Y finalmente hacemos un push del branch master (guardándolo de ahora en más como branch por defecto para los push y pull):

git push -u origin master

Migración de repositorios con esquemas (a) y (b)

En estos esquemas podemos aprovechar la división de tags y branches y exportar los mismos a Git. Hacer esto manualmente demanda bastante trabajo, así que podemos automatizar todo con un script:

  1. !/bin/sh

set -e git(){

   (tput bold; tput setf 2; echo git "$@";	tput sgr0) 1>&2

$(which git) "$@" }

ask(){ tput bold; tput setf 4 read -p "$1 [S/n]: " answer tput sgr0 case "$answer" in N|n) return 1 ;; S|s|"") return 0 ;; *) echo "Respuesta no válida" ask "$1" return $? esac return 0 }


fetch(){ # http://stackoverflow.com/questions/2244252/importing-svn-branches-and-tags-on-git-svn # supply proper trunk, braches and tags folder names and fetch svn repo: git svn init -t tags -b branches -T trunk "https://svn.servidor.com/svn/lihuen/$1" git svn fetch

# Since tags in svn are real braches, create git tags from tag branches: git for-each-ref '--format=%(refname:short) %(objectname)' refs/remotes/tags | cut -d / -f 2- | while read ref do [ "$ref" = "remotes/tags" ] && continue # Descarto esta ref git tag -a svn_$ref -m 'import tag from svn' done

# Delete tag branches

git for-each-ref '--format=%(refname:short)' refs/remotes/tags | cut -d / -f 2- | while read ref do [ "$ref" = "remotes/tags" ] && continue # Descarto esta ref git branch -rd tags/$ref done # Since tags marked in the previous step point to a commit "create tag", we need to derive "real" tags, i.e. parents of "create tag" commits.

git for-each-ref '--format=%(refname:short)' refs/tags | while read ref do tag=`echo $ref | sed -r 's/^svn_//g'` # give tags a new name echo $ref -\> $tag git tag -a $tag `git rev-list -2 $ref | tail -1` -m "proper svn tag" git tag -d svn_$tag # Elimino el tag temporal done

# Convierto los branches remotos en branches locales git for-each-ref refs/remotes | grep -v '@' |grep -v 'remotes/trunk$' | cut -d/ -f3- | while read ref; do git branch "$ref" "remotes/$ref" done }

push(){

echo -- Tags git tag -l echo -- Branches git branch -v

ask "Si la información anterior es correcta ingresá S para seguir" || exit 0

while true; do read -p "Ingresá el namespace del proyecto (por ej. tu nombre de usuario): " usuario ask "¿El namespace a usar es {$usuario}?" || continue break done echo "Crea en gitlab (desde la web) un repositorio con el nombre $usuario/$1, después presioná [Enter] para seguir" read junk

git remote add origin "git@git.servidor.com:$usuario/$1.git" git push origin --mirror }


if [ $# -ne 1 ]; then echo "Falta el nombre del repo svn" echo "Por ejemplo: $0 lihuenconfig" exit 1 fi if [ -n "$(echo $1 | grep -E '/$')" ]; then echo "El nombre del repositorio no puede terminar en /" exit 2 fi

ORIGINAL="$PWD" FETCH=1 if [ -d "$1" ]; then ask "El directorio $1 ya existe ¿subo lo que está ahí a gitlab?" || exit 1 FETCH=0 fi


if [ $FETCH -eq 1 ]; then mkdir "$1" cd "$1" fetch "$1" # Descarga el repo svn usando git-svn y acomoda los tags y branches ask "Importado a repo GIT ¿Querés seguir en el asistente para subirlo al gitlab?" || exit 0 fi [ $FETCH -eq 0 ] && cd "$1" push "$1" # Agrega un repo git como remote y sube todo al mismo (con --mirror)

cd $ORIGINAL