(→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> |
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:
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