Control de Versiones con Subversion

Subversion es el sistema de control de versiones que utilizaremos a través de este curso. No depende ni tiene nada que ver con Ruby o Rails, de hecho usted puede utilizar subversion con cualquier lenguaje de programación (incluso con cualquier colección de archivos para los cuales usted desee mantener un historial de versiones).

Nota: Esto es sólo una introducción básica a subversion para que pueda comprender los comandos de subversion que ejecutaré de vez en cuando en el curso de rails. El tema de control de versiones en sí mismo es un tema enorme. Para una mucha mejor explicación en español de cómo funciona subversion, el libro en línea "Control de Versiones con Subversion" es mucho más adecuado (y también es gratuito). 

Programación y Versiones

Si usted ha escrito programas con anterioridad seguramente le resultará familiar la historia de Juan López, programador:

Ayer como a las 10:00pm mi programa funcionaba perfectamente. Estaba yo haciendo cambios pequeños, nada más para limpiar el código, y me quedé hasta las 2 de la mañana haciendo mejoras estéticas pequeñitas.

Ahora son las 7:00am y tengo una demostración con el cliente en una hora, ¡¡¡y el programa no funciona!!! No se que le hice, pero ojalá hubiera hecho una copia a las 10:00pm.

Este es uno de los muchos motivos por los que todo programador necesita un sistema de control de versiones. Con un sistema de control de versiones, nuestro hipotético Juan López podría haber vuelto a la versión que funcionaba perfectamente a las 8 de la noche y no hubiera perdido el cliente.

La otra ventaja de un sistema de control de versiones es la posibilidad de comparar. Con un sistema de control de versiones, Juan hubiera podido hacer una comparación de los archivos de su versión con la versión de las ocho de la noche, para ver únicamente las diferencias. Como sólo necesitaría examinar las diferencias, podría haber encontrado el error rápidamente y no tendría que perder muchos de sus cambios estéticos para recuperar el funcionamiento de su programa.

Subversion

En este curso utilizaremos el sistema de control de versiones llamado subversion. Subversion es un sistema diseñado para reemplazar otro sistema de control de versiones llamado CVS. CVS es el sistema de control de versiones usado en gran parte del mundo de la programación de fuente abierta. Varios de los comandos que utilizaremos aquí también son válidos en CVS.

Instalación en Unix

Si usted está en un sistema Unix, primero revise su versión de subversion - algunas distribuciones de Unix ya lo tienen. Trate de ejecutar svn --version para ver si el programa responde. SI no lo tiene, puede descargar los códigos fuentes o los programas binarios de la página de web de subversion. Una vez instalado y configurado puede usted continuar para crear su repositorio.

Instalación en Windows

Para los usuarios de Windows hay dos opciones: Usted puede utilizar los binarios de línea de comando, o una extensión para Windows Explorer llamada TortoiseSVN, que le proporcionará menús de contexto. Recuerde que todas las demás plataformas utilizan línea de comando. Este capítulo explica cómo utilizar la línea de comando. En tortoiseSVN esto normalmente quiere decir hacer click con el botón derecho en Windows y seleccionar el comando con el mismo nombre en el menú de contexto.

Creando un Repositorio

Un repositorio es un área en su sistema donde el sistema de control de versiones va a guardar su programa. Su programa se guarda en una serie de archivos que guardan toda la historia de su programa. Usted puede accesar toda la historia de las versiones de su programa.

Para crear un repositorio en subversion, usted puede utilizar el comando svnadmin create:

$ svnadmin create /datos/repositorios/direcciones

Esto creará un repositorio llamado direcciones en el directorio datos/repositorio (que primero debe existir). De preferencia (pero no obligatoriamente) usted debe hacer esto en un servidor, para que se pueda conectar a el repositorio desde otras máquinas.

Importando nuestro código

Ahora que su repositorio ha sido creado, podemos importar nuestro contenido. Con anterioridad hemos creado una aplicación de rails que va a ser una libreta de direcciones, y creamos nuestro modelo de personas junto con el andamiaje. Todo funciona bien hasta ahora, así que lo que queremos es que esta sea la primera "revisión" de nuestro programa. Así que importamos nuestro código fuente del programa de direcciones especificando donde vive nuestro archivo:

$ cd direcciones
$ svn import file:///datos/repositorios/direcciones

¿Porqué "file://"/?
Subversion utiliza URNs para especificar donde vive su repositorio. Cuando usted utiliza svn sin un servidor, el prefijo es file://, para acceder al sistema de archivos local. En un ambiente multiusuario es típico utilizar urns con http:// o ssh://.

A continuación svn abrirá un editor de textos (en Unix, el editor de textos es típicamente vi, o el editor especificado en la variable $EDITOR), que contiene lo siguiente:


--This line, and those below, will be ignored--

A .

La "A" junto al punto decimal quiere decir que estamos Añadiendo el directorio actual (representado por un punto). Es la manera en la que svn nos dice que va a ocurrir una vez que salgamos del editor de textos. El motivo por el cual el editor de textos es mostrado es para que usted describa los cambios que le ocurrieron al repositorio (para el archivo histórico).

Usted ahora puede escribir una descripción de lo que le está haciendo al repositorio. En este caso, estamos apenas comenzando, así que escribimos algo como lo siguiente:

Importando el sistema de direcciones
--This line, and those below, will be ignored--

A .

Ahora guarde el archivo y salga del editor. A continuación verá que subversion añade todos los archivos en su directorio al repositorio: 

Adding         test
Adding test/unit
Adding test/unit/persona_test.rb
Adding test/test_helper.rb
Adding test/functional
Adding test/functional/personas_controller_test.rb
Adding test/fixtures
Adding test/fixtures/personas.yml
Adding test/mocks
Adding test/mocks/test
Adding test/mocks/development
Adding app
Adding app/helpers
Adding app/helpers/application_helper.rb
Adding app/helpers/personas_helper.rb
Adding app/models
Adding app/models/persona.rb
Adding app/controllers
...
Adding public/favicon.ico

Committed revision 1.

Nuestro repositorio ahora ha sido importado de este directorio. Ahora necesitamos sincronizar con el repositorio para que pueda mantener nuestros cambios. Para hacer eso necesitamos primero jalar una copia que provenga del repositorio, así que cambiamos el nombre del directorio de nuestro programa (lo borraremos después que nos aseguremos que todo funciona bien) y ejecutamos el comando svn checkout para que el sistema utilice nuestro repositorio:

$ cd ..
$ mv direcciones direcciones.borrame
$ svn checkout file:///datos/repositorios/direcciones
A direcciones/test
A direcciones/test/unit
A direcciones/test/unit/persona_test.rb
A direcciones/test/test_helper.rb
A direcciones/test/functional
A direcciones/test/functional/personas_controller_test.rb
A direcciones/test/fixtures
A direcciones/test/fixtures/personas.yml
A direcciones/test/mocks
A direcciones/test/mocks/test
A direcciones/test/mocks/development
...
Checked out revision 1.
$ cd direcciones

Modificando Archivos

Ahora tenemos una copia que proviene del repositorio. Esta copia será actualizada cuando nosotros ejecutemos el comando svn commit desde el directorio de direcciones. Podemos actualizar subdirectorios independientemente, depende de donde ejecutemos el comando commit. Además, cambios a varios archivos en el repositorio se realizan atómicamente (se toman en cuenta como uno). Por ejemplo, haga algunos cambios a su archivo doc/README_FOR_APP con un editor de texto (este archivo es útil para describir de que se trata su programa y como instalarlo) y después ejecute svn commit:

$ svn commit

Ahora verá que su editor abre y muestra que ha habido un cambio al archivo:


--This line, and those below, will be ignored--

M doc/README_FOR_APP

Escriba de que se trató el cambio (por ejemplo, "Mejor explicación de nuestro programa") y salga del editor. Subversion guardará el cambio.

Sending        doc/README_FOR_APP
Transmitting file data .
Committed revision 2.

Ahora el repositorio se encuentra en la revisión 2. 

¿Qué significan A y M?
Subversion utiliza letras para explicarle qué le ocurre a los archivos. Las letras son como sigue:

A Este archivo ha sido añadido (Added)
M Este archivo ha sido modificado (Modified)
D Este archivo ha sido elimidado (Deleted)

Historial de un archivo

Para ver la historia de un archivo, utilice el comando svn log:

$ svn log  doc//README_FOR_APP
------------------------------------------------------------------------
r2 | Owner | 2006-01-21 15:39:14 -0800 (Sat, 21 Jan 2006) | 2 lines

Mejor explicacion del programa

------------------------------------------------------------------------
r1 | Owner | 2006-01-21 15:28:01 -0800 (Sat, 21 Jan 2006) | 3 lines

Importando el sistema de direcciones


------------------------------------------------------------------------

El sistema le explica los cambios que incluyeron este archivo.

Añadiendo y eliminando archivos

Cuando usted añade y elimina archivos dentro de su sistema, el sistema de control de versiones no efectúa los cambios hasta que usted especifique el comando svn add o svn rm (añadir y eliminar, respectivamente). Así usted no se necesita preocupar en eliminar un archivo accidentalmente, y de la misma manera no añadirá un archivo por error "ensuciando" la historia de su repositorio.

Por ejemplo, hagamos un archivo de prueba en el directorio doc. Digamos que su nombre es prueba.txt 

$ echo "Hola como estas" > doc/prueba.txt
$ svn add doc/prueba.txt
A doc/prueba.txt
$ svn commit
Adding doc/prueba.txt
Transmitting file data .
Committed revision 3.

Ahora lo podemos eliminar:

$ svn rm doc/prueba.txt
D doc/prueba.txt
$ svn commit
Deleting doc/prueba.txt
Committed revision 4.

Actualizando el Repositorio

Otros programadores (o usted mismo en otra computadora) pueden cambiar su repositorio. Al principio de cada sesión de trabajo, usted puede ejecutar el comando svn update para sincronizar de nuevo con el repositorio:

$ svn update
U doc/README_FOR_APP

En este ejemplo, alguien cambió README_FOR_APP y ahora ya tengo sus cambios.


Comparando versiones

Usted puede comparar versiones utilizando el comando svn diff. Si usted no especifica sólo el nombre del archivo, el sistema comparará la copia del archivo en su disco duro con la copia más nueva del repositorio.

Por ejemplo, algunos cambios a la versión en disco duro de mi doc/README_FOR_APP. Ahora ejecutaré svn diff:

$ svn diff doc/README_FOR_APP
Index: doc/README_FOR_APP
===================================================================
--- doc/README_FOR_APP (revision 2)
+++ doc/README_FOR_APP (working copy)
@@ -1,2 +1,3 @@
-Programa de ejemplo para el curso de rails.
+Esta es una nueva línea

En este listado, las líneas que se eliminaron comienzan con el signo de menos y las que se añadieron con el signo de más. Las arrobas me dicen aproximadamente donde (línea y columna) se encuentra el cambio.

Finalmente, no quiero quedarme con este cambio, así que mejor ejecuto svn revert para que mi copia se elimine y se reemplace con la del repositorio.

$ svn revert doc/README_FOR_APP
Reverted 'doc/README_FOR_APP'

Esto es sólo una introducción, y subversion tiene muchos otros comandos. Le recomiendo que revise la documentación y aprenda lo más que pueda de este excelente sistema.