20 mayo 2013

Tutorial básico de SVN / subversion

A continuación una pequeña minireceta sobre cómo crear un pequeño repositorio de subversión en un sistema remoto, donde alojar tus pequeños proyectos de software o webs.
Este sistema te permitirá tener siempre online una copia actualizada tu código y evitar el tener que llevar .tar.gzs de las últimas versiones de un ordenador a otro. También puede servir como repositorio, en un entorno laboral, de webs, scripts o código de cualquier tipo.

Instalar subversion

Es tan sencillo como instalar el paquete correspondiente vía apt-get o yum
# apt-get install subversion subversion-tools 

# yum install subversion
Ahora debemos optar entre 2 opciones:

1.- Levantamos el daemon de svnserve y creamos un repositorio global en el servidor para todos los usuarios (repositorio por defecto). Esto implica que la gestión de los usuarios se realizará a nivel de servidor (por ejemplo en un fichero de usuarios).
2.- Dejamos que cada usuario aloje sus proyectos en uno o varios repositorios propios en su HOME. Esto sólo necesita acceso SSH abierto en el servidor.


Crear repositorio por defecto (SVN)

Si vamos a ejecutar subversión sin SSH, directamente contra el servidor svn, necesitaremos un script de parada y arranque y un repositorio por defecto. Creamos el siguiente script:
# cat /etc/init.d/svnserve
#!/bin/sh
#
### BEGIN INIT INFO
# Provides:          svnserve
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     
# Default-Stop:      
# X-Interactive:     true
# Short-Description: Start/stop svnserve
### END INIT INFO

test -f /usr/bin/svnserve || exit 0

### Podemos cambiar el puerto en el que escuchar:
OPTIONS="-d -r /var/svn/ --listen-port=3690"

case "$1" in
start)
   echo -n "Starting subversion daemon:"
   echo -n " svnserve"
   start-stop-daemon --start --quiet --oknodo --user root --exec /usr/bin/svnserve -- $OPTIONS
echo "."

;;
stop)
   echo -n "Stopping subversion daemon:"
   echo -n " svnserve"
   start-stop-daemon --stop --quiet --oknodo --exec /usr/bin/svnserve
   echo "."
;;
reload)
;;
force-reload)
   $0 restart
;;
restart)
   $0 stop
   $0 start
;;
*)
   echo "Usage: /etc/init.d/subversion {start|stop|reload|restart}"
   exit 1
;;
esac

exit 0
Damos permisos de ejecución:
# chmod +x /etc/init.d/svnserve
Ahora lo introducimos en el arranque de la máquina:
Debian:     # update-rc.d svnserve defaults

CentOS:     # ntsysv
También debemos definir y crear el repositorio por defecto para el daemon que corre en memoria, y modificar los permisos de acceso autenticado (auth-access → permitimos escritura), anónimo (anon-access → no permitimos el accesos) y los usuarios/passwords de autenticación en fichero aparte:
# svnadmin create /var/svn/

# egrep -v "^#|^$" /var/svn/conf/svnserve.conf
[general]
auth-access = write
anon-access = none
password-db = passwd

# cat /var/svn/conf/passwd 
[users]
miusuario = elpassword
Como luego veremos, nada impide que vía SSH cada usuario pueda tener sus propios repositorios adicionales.
Finalmente, arrancamos el servicio SVN:
# /etc/init.d/svnserve start
En algunos tutoriales se aconseja crear los siguientes directorios base:
# svn mkdir file:///var/svn/trunk -m "Crear trunk"
# svn mkdir file:///var/svn/tags -m "Crear tags"
# svn mkdir file:///var/svn/branches -m "Crear branches"
La ventaja de este sistema es que podemos dar acceso a múltiples usuarios gracias al fichero de passwords, así como dar acceso anónimo de sólo lectura.

Crear repositorios individuales para los usuario (SVN+SSH)

Podemos utilizar SVN por SSH si queremos un acceso “personal” a un repositorio a través de nuestra cuenta de trabajo de un servidor o host Linux sin necesitar acceso administrativo (siempre y cuando tenga instalado el paquete de subversion). Normalmente es la forma más utilizada para usuarios individuales (no trabajo en grupo) que quieren guardar sus proyectos o ficheros en un repositorio para evitar el traspaso de ficheros entre diferentes equipos de trabajo (ejemplo: tener la misma copia de código en casa o en el trabajo).
En el caso de utilizar SVN por ssh, no necesitamos arrancar el daemon ni crear este repositorio por defecto. En ese caso, crearemos uno o varios repositorios para nuestro usuario, ubicado donde queramos en el PATH, por ejemplo en /home/usuario/svn:
Si necesitamos acceso de múltiples usuarios vía SSH, lo mejor es crear un usuario común (no nuestro usuario personal) que alojará el/los repositorios.
En el caso de SVN+SSH se nos presentan 2 opciones relativas a la organización de los repositorios:


1.- Crear un único repositorio del que colgarán todos los proyectos:

Creamos un directorio único en el servidor (por ejemplo /home/usuario/svn), creado con “svnadmin create”, el cual alojará todos los proyectos. La raíz del repositorio será dicho directorio, para todos los proyectos. La ventaja de este sistema es la simplicidad del “setup”.
La desventaja de este sistema es que las revisiones del repositorio son comunes a todos los proyectos; es decir, si en un repositorio con 3 proyectos hacemos commit en cada proyecto, la revisión aumenta en 3, no es específica para cada proyecto.
Además, borrar un proyecto es “complicado”: implica regenerar el repositorio a partir de un dump con filtro extraído con “svndumpfilter exclude” (ver reposadmin.maint.filtering).


2.- Crear múltiples repositorios (un repositorio para cada proyecto):

Creamos un directorio único en el servidor (por ejemplo /home/usuario/svn), y dentro de dicho directorio creamos un repositorio para cada proyecto (con “svnadmin create nombreproyecto”). La raíz del repositorio ya no será /home/usuario/svn sino /home/usuario/svn/PROYECTO, según el proyecto con el que queramos trabajar.
La ventaja de este sistema es que cada proyecto tiene su propio sistema de revisiones, y sus propios tags y branches. También, el borrado de los proyectos (repositorios) es muy fácil y simple.
Yo personalmente me decanto por esta opción aunque veremos las 2.
La opción elegida sólo cambia la forma de CREAR EL REPOSITORIO y de hacer LA SUBIDA INICIAL DEL PROYECTO y la DESCARGA INICIAL DEL PROYECTO (digamos que los primeros 10 minutos de uso del repositorio). Después, el uso diario y regular de SVN es igual en ambas opciones.

Crear un único repositorio donde alojar todos los proyectos

Creamos el repositorio en el servidor:
$ mkdir /home/usuario/svn/

$ svnadmin create /home/usuario/svn
Una vez hecho esto, vía SSH podemos ya acceder al repositorio referenciándolo como:
$ export SVNROOT="svn+ssh://usuario@server.com/home/usuario/svn/"
De nuevo, se recomienda crear los siguientes subdirectorios:
$ svn mkdir file:///home/usuario/svn/trunk -m "Crear trunk"
$ svn mkdir file:///home/usuario/svn/tags -m "Crear tags"
$ svn mkdir file:///home/usuario/svn/branches -m "Crear branches"

Repositorio único: Subir por primera vez un proyecto al repositorio

Sea un directorio de código fuente denominado “miproyecto”. Nos situamos en el directorio padre de “miproyecto” y ejecutamos uno de los siguientes comandos:
$ svn import miproyecto svn://server.sromero.org/var/svn/miproyecto
$ svn import miproyecto svn+ssh://server.sromero.org/home/sromero/svn/miproyecto
$ svn import miproyecto ${SVNROOT}/miproyecto
$ svn import miproyecto ${SVNROOT}/miproyecto -m "Upload inicial del proyecto"
Al comando le decimos que importe un directorio específico (svn import miproyecto) y le decimos que lo haga sobre el SVNROOT añadiendo además el subdirectorio del repositorio que alojará el proyecto (miproyecto también en este caso). Esto permite tener múltiples proyectos en cada repositorio, gracias a este path “final” sobre el SVNROOT.
Se nos solicitará entonces la introducción de una línea de texto para “resumir” en qué consiste el cambio realizado. Todos los cambios en el repositorio tienen que tener este “summary” que luego podemos recuperar con “log”.
Como se puede ver en el último ejemplo, podemos agregar el resumen para el log en la línea de comandos (en modo no interactivo) con -m “Upload inicial del proyecto”.

Repositorio único: Descargar una copia del proyecto desde el repositorio

Una vez un repositorio está en un proyecto, podemos “descargar” una copia ya controlada por SVN. De hecho, deberíamos desechar (de momento renombrar) el directorio de proyecto original y trabajar con la copia versionada.
Nos situamos en el directorio de trabajo “padre” y (una vez renombrado el directorio original si existe) ejecutamos lo siguiente (una u otra opción según si tenemos definida o no la variable):
$ svn checkout svn://server.sromero.org/var/svn/miproyecto
$ svn checkout ${SVNROOT}/miproyecto
Esto descargará el proyecto “SVNROOT/miproyecto” recreando el directorio “miproyecto” en el directorio actual.

Usar un repositorio para cada proyecto

Esta puede ser la opción más habitual si no queremos mezclar revisiones y si queremos poder borrar un proyecto concreto.

Múltiples repositorios: creando los repositorios

  • Creamos un directorio común del que colgarán todos los repositorios (proyectos). Por ejemplo: /home/usuario/svn/.
  • Creamos un repositorio por cada proyecto, con “svnadmin create /home/usuario/svn/proyecto”.
  • Preparamos cada proyecto para su subida, creando los subdirectorios trunk, branches y tags. Esta subida implicará un SVNROOT diferente para cada proyecto.
  • Subimos cada proyecto a su propio repositorio.
  • Ya podemos trabajar con los proyectos. Para esto ya no hará falta definir un SVNROOT diferente por proyecto ya que los directorios ocultos .svn de cada proyecto guardan esta información:

Empezamos creando el directorio común en el servidor SVN:
$ mkdir /home/usuario/svn/
Para cada proyecto a alojar hay después que crear su repositorio en el servidor SVN:
$ svnadmin create /home/usuario/svn/proyecto1 
$ svnadmin create /home/usuario/svn/proyecto2

Múltiples repositorios: Subir por primera vez proyectos al repositorio

Supongamos que tenemos en nuestro sistema 2 proyectos bajo /home/usuario/code y queremos alojarlos en el servidor de SVN.
Primero preparamos el proyecto1, creando los 3 subdirectorios trunk, branches y tags:

  • trunk → contiene el código fuente del proyecto.
  • branches → donde se pueden crear ramas del proyecto.
  • tags → donde se pueden alojar “snapshots” del código fuente en un momento dado.


$ cd /home/usuario/code/
$ cd proyecto1
$ mkdir trunk
$ mv * trunk
$ mkdir branches
$ mkdir tags
Una vez estructurado el directorio del proyecto, lo subimos al repositorio:
$ export SVNROOT="svn+ssh://server.sromero.org/home/sromero/svn/proyecto1"
$ svn import proyecto1 ${SVNROOT}/ -m "Agregamos proyecto1 a su propio repositorio"
Nótese que ahora el SVNROOT incluye el subdirectorio “proyecto1”, al contrario que en el caso de múltiples proyectos en un único repositorio.
Repetimos a continuación el proceso para nuestro segundo proyecto:
$ cd /home/usuario/code/
$ cd proyecto2
$ mkdir trunk
$ mv * trunk
$ mkdir branches
$ mkdir tags
$ export SVNROOT="svn+ssh://server.sromero.org/home/sromero/svn/proyecto2"
$ svn import proyecto1 ${SVNROOT}/ -m "Agregamos proyecto2 a su propio repositorio"

Múltiples repositorios: Descargar por primera vez el proyecto del repositorio

Importamos el proyecto “subversionado”, renombrando el directorio original y haciendo un checkout:
$ cd /home/usuario/code/
$ export SVNROOT="svn+ssh://server.sromero.org/home/sromero/svn/proyecto1"
$ mv proyecto1 proyecto1.orig
$ svn checkout $SVNROOT proyecto1
Y lo mismo para el otro proyecto:
mv proyecto2 proyecto2.orig
svn checkout $SVNROOT proyecto2
Ahora ya podríamos trabajar con normalidad dentro de cualquiera de los 2 proyectos (svn update, svn commit, etc), y cada uno de ellos estará alojado dentro de su propio repositorio sin compartir estructuras de SVN con otros proyectos.

Múltiples repositorios: Borrado de un proyecto del repositorio

El borrado de un proyecto, se realizará en el servidor con un simple:
$ rm -rf /home/usuario/svn/elproyecto
(sin afectar así al resto de proyectos, y pudiendo volver a generarlo de nuevo con un “svnadmin create” si fuera necesario).

Múltiples repositorios: Import y checkout inicial masivo

Veamos un pequeño “script” que subirá al repositorio todos los proyectos que tenemos bajo un mismo directorio:
En el servidor SVN:
cd /home/usuario/svn

for PROJECT in proyecto1 proyecto2 proyecto3 proyecto4;
do
    svnadmin create $PROJECT
done
En nuestro equipo de trabajo:
cd /home/usuario/prog/code

for PROJECT in proyecto1 proyecto2 proyecto3 proyecto4;
do
    export SVNROOT="svn+ssh://user@myserver.org/home/usuario/svn/${PROJECT}/"
    cd ${PROJECT}
    mkdir trunk
    mv * trunk      
    mkdir tags
    mkdir branches
    cd ..
    svn import $PROJECT ${SVNROOT} -m "Agregamos proyecto al repositorio"
done
Con esto hemos subido los diferentes proyectos al servidor SVN, cada uno en un repositorio diferente bajo la misma ruta.
Ahora tenemos que descargarnos los proyectos “subversionados” para trabajar con ellos en lugar de usar los directorios de código fuente originales (que no contienen el directorio oculto .svn):
cd /home/usuario/prog/code

for PROJECT in proyecto1 proyecto2 proyecto3 proyecto4;
do
   # Movemos el directorio original como .orig
   if [ -e $PROJECT ]; then mv $PROJECT ${PROJECT}.orig ; fi

   export SVNROOT="svn+ssh://user@myserver.org/home/usuario/svn/${PROJECT}/"

   svn checkout $SVNROOT $PROJECT
done
Esta operación es la misma que tendremos que realizar en otros equipos (ej: en casa) donde guardemos copia de estos proyectos, para tenerlos versionados y apuntando al repositorio desde este momento.
Un apunte final sobre el comando mv del script importador:
    mv * trunk      
Este comando no movera archivos ocultos a trunk/ si tu proyecto los tiene. En ese caso, se puede usar:
(shopt -s dotglob; mv -- * ..)
(con los paréntesis)

Usar subversion


Referenciar el repositorio:

La referencia del repositorio puede ser svn: o svn+ssh: :


  • SVN puerto por defecto:
svn://server.sromero.org/var/svn/
  • SVN puerto modificado:
svn://server.sromero.org:puerto/var/svn/
  • SVN via SSH puerto estándar:
Repositorio único:       svn+ssh://server.sromero.org/home/usuario/svn/
Múltiples repositorios:  svn+ssh://server.sromero.org/home/usuario/svn/nombre_proyecto
  • SVN via SSH puerto o usuario modificados:
Repositorio único:       svn+ssh://server.sromero.org/home/usuario/svn/
Repositorio único:       svn+ssh://usuario@server.sromero.org/home/usuario/svn/
Múltiples repositorios:  svn+ssh://usuario@server.sromero.org/home/usuario/svn/nombre_proyecto

export SVN_SSH="ssh -p 24" 
Repositorio único:       svn+ssh://usuario@server.sromero.org/home/usuario/svn/
Múltiples repositorios:  svn+ssh://usuario@server.sromero.org/home/usuario/svn/nombre_proyecto


En ocasiones puede ser útil declarar este valor en un “export SVNROOT” para usarlo después en las llamadas a svn.
SVN/SSH no nos pedirá password si utilizamos autenticación sin password por clave pública/privada. En el caso de SSH, en ocasiones se crea un usuario específico “svn” para que sea dueño específico del repositorio.

Subir cambios al repositorio

Cuando hacemos cambios en el código, debemos de subirlos al repositorio (hacer un commit de los cambios). Hay quien hace commit cada vez que hace un cambio acotado (para que gracias al comentario del commit podamos saber qué cambios se realizan cada vez), y hay quien hace un commit al final de largas sesiones de trabajo.
Una vez hayamos decidido hacer el commit, y estando dentro del directorio de trabajo del proyecto, ejecutamos:
$ svn commit
$ svn commit fichero_o_directorio
$ svn commit -m "Resumen de cambios"
Nótese que no necesitamos especificar el SVNROOT porque viene ya “pregrabado” en el directorio que hemos descargado (directorio oculto .svn).

Descargar cambios desde el repositorio

Si otros compañeros han subido cambios o bien queremos descargar en casa los cambios realizados en el trabajo, podemos hacer un update sobre nuestra copia local (que en algún momento fue descargada con checkout).
$ svn update
$ svn update fichero_o_directorio 

Agregar/Crear/Mover/Borrar ficheros o directorios al proyecto

En SVN tenemos que “añadir” al repositorio los ficheros NUEVOS que creemos localmente, para que sean también subidos/descargados con los checkouts/updates. También hay que eliminar ficheros remotamente si queremos borrarlos localmente.
$ svn add fichero_o_directorio
$ svn mkdir nuevo-dir
$ svn move file_dir1 file_dir2
$ svn delete fichero_o_directorio

Ver las diferencias de nuestra copia respecto al repositorio

Si hemos hecho cambios en nuestra copia local “descargada”; podemos ver un diff de dichos cambios con:
$ svn diff
$ svn diff fichero_o_directorio

$ svn diff | less
$ svn diff | vim -

Descartar nuestros cambios locales

Es posible en algún momento que hayamos hecho cambios en algún fichero de nuestra copia local y queramos deshacerlos para empezar de cero con los datos que hay en el repositorio. En ese caso no es necesario realizar un checkout de nuevo, podemos hacer un:
$ svn revert fichero_o_directorio
O bien, si queremos revertir todos los cambios (.) o los de un directorio:
$ svn revert --recursive .
$ svn revert --recursive directorio

Ver historico de un fichero o del proyecto

Lo siguiente muestra todo el registro de cambios de un proyecto:
$ svn log
$ svn log fichero_o_directorio


Ver estado del proyecto

El siguiente comando proporciona un listado de nuevos ficheros, ficheros modificados, y ficheros a borrar en el próximo commit.
$ svn status
Resulta también muy útil para conocer si en el directorio actual hay ficheros sin versionar (a los que falta hacerle el svn add), porque estos aparecen con el prefijo ?:
$ touch new_file
$ svn status
?       new_file

Flags opcionales para el comando "svn"

Si no estamos usando svn+ssh, puede ser interesante especificar user/pass en línea de comandos:
--username sromero --password XXXXXX
También, para el checkout y para el update, puede ser útil la opción –force.
Con –force podríamos hacer un checkout de un proyecto recién subido al repositorio con un import sobre el directorio “inicial” sin renombrarlo. Es decir, podríamos subir una web al repositorio y hacer un checkout –force en el directorio de la web para “descargar la copia remota” y crear las estructuras .svn sin tener que renombrar el directorio y hacer el checkout (que cortaría la web mientras hacemos el mv + checkout).

Mover un repositorio ya existente

Para mover un repositorio se puede usar switch:
$ svn switch --relocate old-url new-url


Tags y branches de proyectos

En subversion tenemos disponibles las siguientes funcionalidades:


  • tags → “Snapshot” (foto ó backup) del código (trunk) en un momento dado, para marcar dicho momento en el tiempo del código como un estado concreto (por ejemplo: versión 0.1.0), o para hacer una copia del código antes de realizar alguna acción “invasiva” y de la que podríamos querer volver atrás. Los tags nos permiten así empaquetar el código en versiones específicas o bien volver atrás después de un cambio que no ha salido bien y que no se puede deshacer ya con un revert porque ha tenido commits al repositorio. Los tags son “versiones sólo lectura” del código.
  • branches → “Ramas” del código. Es decir, “versiones paralelas” del código que otro equipo de desarrollo pueda querer explorar. Son parecidos a los tags, salvo que pueden modificarse. Por ejemplo, podríamos hacer un branch del código para que otro equipo de desarrollo pruebe a implementar soporte en nuestro programa para otro Sistema Operativo, y que si el desarrollo llega a buen puerto, se haga un “merge” de esta rama con el trunk.


Para crear un tag o un branch, lo haríamos con el comando “svn copy
$ svn copy trunk tags/0.1.0 -m "Version 0.1.0 del proyecto"
$ svn commit -m "Version 0.1.0" tags/

$ svn copy trunk tags/0.1.0-antes-de-probar-SDL -m "Snapshot antes de probar SDL"
$ svn commit -m "Snapshot antes de probar SDL" tags/

$ svn copy trunk branches/soporte-android -m "Creamos branch pruebas Android"
$ svn commit -m "Branch pruebas de soporte para Android" branches/
También podemos crear un tag o branch no desde la rama de trunk sino desde nuestra copia local (directorio del proyecto), es decir, con cambios aún no subidos al repositorio:
$ svn copy miproyecto ${SVNROOT}/tags/android-funcional -m "Tag con codigo Android Funcional"
Para borrar un tag o un branch, lo haríamos con “svn delete”:
$ svn delete tags/0.1.0-antes-de-probar-SDL -m "Eliminamos el tag: SDL implementado OK"
$ svn commit -m "Borrado tag 0.1.0-antes-de-probar-SDL"

$ svn delete branches/soporte-android
$ svn commit -m "Se borra la rama de soporte para Android"
En realidad, físicamente no se borra el código, sólo se marca el repositorio de forma que ya no “exista” en cuanto a actualizaciones. No obstante, podemos recuperar cualquier número de revisión en cualquier momento con:
$ svn copy ${SVNROOT}/branches/rama-android@15 \
           ${SVNROOT}/branches/rama-android \
           -m "Restaurando revision 15"
Podemos obtener un listado de tags o branches del proyecto con svn list (para restaurar revisiones puede sernos útil también svn log, svn info y svn status).
$ svn list ${SVNROOT}/tags 
0.1.0/
Y podemos hacer un checkout de un release concreto con:
$ svn checkout ${SVNROOT}/tags/0.1.0 
Una de las pegas de los tags es que son parte del proyecto por lo que cada vez que hagamos checkouts o updates nos los vamos a descargar en ~/proyecto/tags . Si queremos evitar esto, podemos hacer checkout sólo del raíz del repositorio (no recursivamente, -N) y actualizar sólo el trunk:
$ svn checkout $SVNROOT -N PROYECTO
$ svn update PROYECTO/trunk
De esta forma sólo descargamos el trunk, y no tags ni branches, por lo que no desperdiciamos espacio en disco local.

Trabajando con ramas (branches)

Existen diferentes políticas al respecto de cómo trabajar con el código y las ramas.
Quizá la más extendida es trabajar sobre la rama principal (trunk) y cada cierto tiempo crear una rama denominada “estable”. El proyecto se continúa después sobre la rama principal y la estable sólo se modificará (durante el tiempo de vida de dicha versión) para corregir bugs (que también se deberán corregir en el trunk si le afectan); las nuevas características o funcionalidades sólo se implementarán en la rama principal.
Así, podemos crear una nueva rama y trabajar sólo sobre ella así:
$ svn info ${SVNROOT}/trunk | grep Revision
# (anotamos el numero de revision actual "REVISION_ACTUAL")

$ svn copy ${SVNROOT}/trunk ${SVNROOT}/branches/stable-1.0 \
      -m "Rama estable LA_REVISION_ACTUAL"
Ahora configuramos nuestra copia local como la nueva rama:
svn switch --relocate ${SVNROOT}/trunk ${SVNROOT}/branches/stable-1.0
En este momento, podemos trabajar con nuestra copia local y ya no estaremos trabajando con el trunk sino con “stable-1.0”.
$ svn info | grep URL
$ svn update
# (hacemos cambios)
$ svn commit -m "Bug corregido en blah"
Estos pasos servirán incluso si tenemos cambios en nuestra copia local que todavía no hemos subido al trunk, porque hemos decidido que estén en la nueva rama y no en el trunk.


Convertir nuestra rama en la rama principal


También podemos haber querido crear una rama para explorar una alternativa al código (por ejemplo, cambiar de GTK a QT) y que los cambios hayan tenido éxito y queramos convertir esta rama en la versión principal:
Un primer método es borrar el trunk y convertir el código de nuestra rama en el trunk:
$ svn delete ${SVNSERVER}/trunk
$ svn move ${SVNSERVER}/branches/programa-con-qt ${SNVSERVER}/trunk
$ svn switch --relocate ${SVNSERVER}/branches/programa-con-qt ${SVNSERVER}/trunk


Hacer merge de los cambios de nuestra rama con la principal


El merge de una rama al trunk se realiza con:
$ svn merge -r XXX:YYY $SVNROOT/branches/la-rama
$ svn commit -m "Realizado merge de la-rama (XXX:YYY) al trunk"
Siendo XXX la revisión en la cual nació la rama y YYY la revisión actual del trunk.
Para obtener estos datos, podemos:
# hacer un checkout del trunk y de la rama en otro directorio:
$ cd ..
$ mkdir temp
$ cd temp
$ svn checkout $SVNSERVER/trunk trunk
$ svn checkout $SVNSERVER/branches/la-rama la-rama
$ cd la-rama

# encontrar la revisión donde se creó la rama (XXX):
$ cd la-rama
$ svn log --stop-on-copy

# encontrar la revisión actual del trunk ("At revision YYY"):
$ cd ..
$ cd trunk
$ svn update 
Esto realizará un merge del código de la-rama sobre el trunk. Si hubiera conflictos de edición de ficheros (puede haberlos), los arreglamos localmente y hacemos un commit para subirlos:
$ vim fichero_que_da_conflictos.c
$ svn commit -m "Merge de 
$ svn commit -m "Realizado merge de la-rama (XXX:YYY) al trunk"

Borrar un repositorio ya existente en el servidor

Simplemente, ejecutampos lo siguiente en el repositorio:
# TODO EL ARBOL DE PROYECTOS/REPOSITORIOS
$ rm -rf /home/sromero/svn/

# UN REPOSITORIO CONCRETO:
$ rm -rf /home/sromero/svn/PROYECTO/
Para volver a tenerlo operativo habría que seguir de nuevo el proceso de creación (mkdir, svnadmin create, editar conf/svnserve.conf, e importar en él proyectos).

Operativa inicial con SVN (cómo subir un proyecto)

Según si vamos a crear un repositorio común para todos los proyectos, o si vamos a crear un repositorio por cada proyecto, los comandos exactos son ligeramente diferentes (ver las correspondientes secciones al principio de este documento), pero generalizando, son:

1.- Crear el/los repositorios en el servidor.
2.- Establecer variables de entorno (SVNROOT y SVN_SSH si necesitamos cambiar el puerto).
3.- Preparar los directorios de nuestro proyecto (en el caso de múltiples repositorios, crear directorios trunk/branches/tags).
4.- Subir nuestro proyecto o web al repositorio.
5.- Renombrar el directorio antiguo.
6.- Descargar el mismo directorio pero con el control de versiones.
7.- Revisar que la descarga es correcta y que no faltan ficheros.
8.- “Archivar” (guardar, borrar) el directorio de código antiguo.

Operativa regular (trabajar con un proyecto)

A partir de ahora, debemos trabajar sobre el directorio de trabajo creado acordándonos de realizar “svn add” de nuevos ficheros, “svn delete” de ficheros que borremos, y de hacer los commits para subir nuestro trabajo y updates para recuperar el trabajo de los demás.


Aplicación práctica: ficheros específicos del HOME en SVN

Una cosa habitual para las personas que trabajamos con varios PCs (portátil, sobremesa casa, trabajo…) es tener gran cantidad de ficheros de configuración comunes entre todos ellos: .vimrc, .bashrc, .tmux.conf, así como directorios de notas, documentación, etc… En esos casos, cada vez que hacemos una mejora en uno de los ficheros, tenemos que distribuirlo manualmente a los otros equipos (vía correo, scp, o llavero USB).
En este ejemplo, vamos a mantener una copia selectiva de ficheros de nuestro HOME en un repositorio privado online de subversion, de forma que con un simple svn update (incluso podría ser automatizado en el login de nuestro usuario) podamos actualizar nuestro HOME local con las últimas copias de dichos ficheros. No sólo eso, sino que nos protege contra borrados accidentales de ficheros (podemos recuperarlos con un svn update) o para encontrar cambios en los ficheros que hayan producido algún problema (volviendo a revisiones anteriores, etc).
NOTA: nunca debemos subir ficheros con datos sensibles en cualquier caso, como una clave SSH, si no confiamos en la seguridad de dicho servidor.
Por ejemplo: estamos en el trabajo, encontramos una opción fantástica para el fichero .vimrc, la añadimos, hacemos un svn commit -m “Nueva opcion vimrc”, y luego en casa sólo hay que hacer svn update (o bien, como he dicho, ponerlo de forma automática en el arranque o el login del usuario).
Para montar este sistema, primero creamos el repositorio en el servidor:
$ svnadmin create /home/usuario/svn/home
Ahora ya en la parte cliente, creamos un directorio “home” ficticio, vacío, para subirlo al repositorio:
$ cd
$ mkdir home
$ export SVNROOT="svn+ssh://usuario@server.org/home/usuario/svn/home"
$ svn import home $SVNROOT -m "Creado homedir"
$ rm -rf ~/home
Finalmente, dentro de nuestro /home/usuario real, hacemos un checkout de “home” para que svn cree un directorio .svn de control en nuestro home.
$ cd /home/usuario
$ export SVNROOT="svn+ssh://usuario@server.org/home/usuario/svn/home"
$ svn checkout $SVNROOT . --force
Revisión obtenida: 0
Este checkout lo tenemos que realizar inicialmente en todos lo /home/usuario de los equipos donde queremos tener sincronizados los ficheros (por ejemplo, casa, trabajo, portátil)…
Ojo, –force sobreescribirá cualquier archivo ya existente, por lo que hay que revisar que no hayamos subido al SVN ninguno que no queramos tener idéntico entre los diferentes equipos (en cualquier caso, se recomienda hacer una copia previa a otro directorio “old” de los ficheros que se sobreescribirán la primera vez, por si acaso).
Desde este momento ya podemos subir ficheros a nuestro repositorio:
$ svn add .vimrc* .tmux.conf .gvimrc
A         .vimrc
A         .vimrc.common
A         .tmux.conf
A         .gvimrc

$ svn commit -m "Subidos ficheros iniciales"
Añadiendo      .gvimrc
Añadiendo      .tmux.conf
Añadiendo      .vimrc
Añadiendo      .vimrc.common
Transmitiendo contenido de archivos ..........
Commit de la revisión 1.
Con esto, cada vez que añadamos alguna funcionalidad nueva en el .vimrc, por ejemplo, en casa, podemos hacer un update en el trabajo y disfrutar de “contenidos sincronizados” sin andar enviando ficheros por correo o en unidades USB.
Simplemente tenemos que hacer un svn update en cada sitio para tener regularmente sincronizados los ficheros que tengamos en el repositorio.
# En otro sistema diferente, 
$ export SVNROOT="svn+ssh://sromero@server.sromero.org/home/sromero/svn/home"
$ svn update --force
Además de ficheros podemos añadir directorios completos con sus subdirectorios:
$ svn add docs
A         docs/notas_subversion.txt
A         docs/comandos_utiles.txt
A         docs/tutorial_truecrypt.txt
(...)

$ svn commit -m "Subido directorio docs"
Si queremos añadir sólo algunos ficheros de un directorio (es decir, ficheros selectivamente, pero no todo el contenido), bastará con que agreguemos el directorio con –non-recursive y luego los ficheros individuales. Esto puede ser útil por ejemplo si tenemos un directorio que tiene algún fichero que DEBE de ser diferente en ambos sistemas:
$ ls -1 bin
haz_backup.sh
sistema_backup.py
wxmylauncher.py

$ svn add bin --non-recursive
A         bin

$ svn add bin/sistema_backup.py bin/wxmylauncher.py
A         bin/sistema_backup.py
A         bin/wxmylauncher.py

$ svn commit -m "Subidos algunos ficheros de bin/"
De esta forma, los updates sólo actualizarán los 2 scripts que hemos marcado, y podemos tener así scripts con el mismo nombre pero distinto contenido en las diferentes máquinas.
Podemos habilitar un script para facilitarnos el “svn update” del home:
$ cat bin/update_home_svn.sh 
#!/bin/bash

# Update home directory from SVN
export SVNROOT="svn+ssh://usuario@server.org/home/usuario/svn/home"
cd $HOME
svn update
Siendo este script otro candidato a mantener en SVN (svn add bin/update_home_svn.sh + svn commit).

Tomado de: http://www.sromero.org/wiki/linux/servicios/subversion/basico

No hay comentarios: