(→Migración de repositorios con esquemas (a) y (b)) |
(→Migración de repositorios con esquemas (a) y (b)) |
||
Línea 185: | Línea 185: | ||
cd $ORIGINAL | cd $ORIGINAL | ||
</source> | </source> | ||
− | |||
[[Categoría:Git]] | [[Categoría:Git]] | ||
[[Categoría:Desarrollo]] | [[Categoría:Desarrollo]] |
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.
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:
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.
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).
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
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:
#!/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