Usando ramas

A estas alturas debería entender que todo cambio en el repositorio genera internamente un nuevo árbol de sistema de ficheros (llamado revisión). Si no es así, debería volver atrás y leer información sobre revisiones en “Revisiones”.

En este capítulo, volveremos al ejemplo del segundo capítulo. Recuerde cómo usted y su compañera, Carmen, comparten un repositorio que contiene dos proyectos, paint y calc. Fíjese que en Figura 4.2, “Estructura inicial del repositorio”, no obstante, cada directorio de proyecto contiene subdirectorios llamados trunk y branches. La razón se descubrirá pronto.

Figura 4.2. Estructura inicial del repositorio

Estructura inicial del repositorio

Igual que antes, asuma que tanto Carmen como usted tienen copias locales del proyecto calc. En concreto, ambos tienen una copia local de /calc/trunk. Todos los ficheros del proyecto están en este subdirectorio en lugar de /calc, porque su equipo ha decidido que /calc/trunk es donde tendrá lugar la línea principal de desarrollo.

Digamos que le han encargado la tarea de realizar una reorganización radical del proyecto. Le tomará un largo tiempo realizar la modificación, y ésta afectará a todos los ficheros del proyecto. En esta situación el problema es que no quiere interferir con Carmen, quien sigue en el proceso de corregir pequeños fallos aquí y allá. Ella depende del hecho que la última versión del proyecto (en /calc/trunk) siempre sea usable. Si comienza a enviar cambios al repositorio poco a poco, seguramente fastidiará la tarea de Carmen.

Una estrategia es esconderse en un agujero: usted y Carmen pueden dejar de compartir información durante una o dos semanas. Es decir, comienza a reorganizar los ficheros en su copia local, pero no envía los cambios al servidor o actualiza su copia hasta que ha completado la tarea. Sin embargo, hay varios problemas con esto. En primer lugar, no es muy seguro. La mayoría de las personas prefieren guardar sus cambios en el repositorio con frecuencia, por si algo malo pudiera suceder de forma accidental a su copia local. Segundo, no es muy flexible. Si realiza su trabajo en diferentes ordenadores (quizás tiene una copia de /calc/trunk en dos máquinas diferentes), necesitará copiar manualmente sus cambios de un lado a otro, o realizar todo el trabajo en un solo ordenador. Del mismo modo, es difícil compartir sus cambios a mitad del desarrollo con otras personas. Una práctica ideal común en el desarrollo de software es permitir que sus compañeros puedan revisar su trabajo a medida que progresa. Si nadie ve los cambios que realiza poco a poco, pierde críticas potenciales. Finalmente, si ha finalizado con su tarea, puede encontrarse con que es muy difícil fusionar su trabajo final con el cuerpo principal del repositorio usado por el resto de la compañía. Carmen (y otros) puede haber realizado cambios en el repositorio que son difíciles de incorporar en su copia local—especialmente si ejecuta svn update semanas tras el aislamiento.

La mejor solución es crear su propia rama, o línea de desarrollo, en el repositorio. Esto le permite guardar su trabajo a medio hacer con frecuencia sin interferir con otros, permitiendo a su vez compartir de forma selectiva información con sus colaboradores. Más adelante verá exactamente como funciona esto.

Creando una rama

Crear una rama es muy simple—realice una copia del proyecto en el repositorio usando el comando svn copy. Subversion no sólo es capaz de copiar ficheros individuales, sino también directorios. En este caso, desea realizar una copia del directorio /calc/trunk. ¿Dónde debería colocar la nueva copia? Donde desee—es una cuestión de política de su proyecto. Digamos que su equipo tiene la política de crear ramas en el área de repositorio /calc/branches, y quiere nombrar la rama my-calc-branch. Entonces querrá crear un nuevo directorio /calc/branches/my-calc-branch, el cual comienza su vida como una copia de /calc/trunk.

Hay dos formas diferentes de realizar una copia. Le mostraremos primero el modo engorroso, para asegurarnos de que queda claro el concepto. Para comenzar, obtenga una copia local del directorio raíz del proyecto, /calc:

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

Realizar ahora una copia es cuestión de pasar dos rutas locales al comando svn copy:

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

En este caso, el comando svn copy copia recursivamente el directorio trunk en un nuevo directorio, branches/my-calc-branch. Tal y como puede ver por el comando svn status, el nuevo directorio está ahora a la espera de ser añadido al repositorio. Pero fíjese en el signo + tras la letra A. Esto indica que la adición pendiente es una copia de algo, no algo nuevo. Cuando envíe sus cambios al repositorio, Subversion creará /calc/branches/my-calc-branch en el repositorio como una copia de /calc/trunk, en lugar de reenviar todos los datos de su copia local de nuevo a través de la red:

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

Y ahora el método más sencillo para crear una rama, que deberíamos haberle explicado en primer lugar: svn copy es capaz de operar directamente sobre dos URLs.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.

Realmente no hay diferencia entre estos dos métodos. Ambos procedimientos crean un nuevo directorio en la revisión 341, y el nuevo directorio es una copia de /calc/trunk. Esto se puede ver en Figura 4.3, “Repositorio con nueva copia”. Note cómo el segundo método, no obstante, realiza los cambios en el repositorio inmediatamente. [14] Es un procedimiento más sencillo, porque no requiere obtener primero una gran copia local de todo el repositorio. De hecho, esta técnica ni si quiera requiere que tenga una copia local en absoluto.

Figura 4.3. Repositorio con nueva copia

Repositorio con nueva copia

Trabajando con su rama

Ahora que ha creado una rama del proyecto, puede obtener una nueva copia local para comenzar a usarla:

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

No hay nada especial sobre esta copia local; simplemente refleja un directorio diferente del repositorio. Cuando envíe sus cambios, no obstante, Carmen nunca los verá cuando se actualice. Su copia local es de /calc/trunk. (Asegúrese de leer “Cambiando la copia local de trabajo” más adelante en este capítulo: el comando svn switch permite crear una copia local de una rama de modo diferente.)

Pretendamos que ha pasado una semana, y se realizan los siguientes cambios:

  • Ha realizado un cambio en /calc/branches/my-calc-branch/button.c, creando la revisión 342.

  • Ha realizado un cambio en /calc/branches/my-calc-branch/integer.c, creando la revisión 343.

  • Carmen ha realizado un cambio en /calc/trunk/integer.c, creando la revisión 344.

Ahora hay dos líneas independientes de desarrollo, mostradas en Figura 4.4, “Bifurcación de la historia de un fichero”, sobre el fichero integer.c.

Figura 4.4. Bifurcación de la historia de un fichero

Bifurcación de la historia de un fichero

Las cosas se ponen interesantes cuando mira el historial de cambios realizados a su copia de integer.c:

$ pwd
/home/usuario/my-calc-branch

$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | usuario | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | usuario | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | carmen | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | carmen | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

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

Fíjese como Subversion sigue la historia de integer.c en su rama hacia atrás, traspasando incluso el punto donde el fichero fue copiado. Muestra la creación de la rama como un evento en la historia, porque integer.c fue copiado de forma implícita cuando realizó la copia de /calc/trunk/. Ahora mire qué es lo que ocurre cuando Carmen ejecuta el mismo comando sobre su copia del fichero:

$ pwd
/home/carmen/calc

$ svn log --verbose integer.c
------------------------------------------------------------------------
r344 | carmen | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  fix a bunch of spelling errors.

------------------------------------------------------------------------
r303 | carmen | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | carmen | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

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

Carmen ve su propio cambio de la revisión 344, pero no el cambio realizado en la revisión 343. Desde el punto de vista de Subversion, estos dos cambios afectaron ficheros diferentes en distintos lugares del repositorio. No obstante, Subversion muestra que ambos ficheros comparten una historia común. Antes de que la rama fuese creada en la revisión 341, ambos eran el mismo fichero. Por esta razón tanto usted como Carmen ven los cambios realizados en las revisiones 303 y 98.

Conceptos clave sobre las ramas

Hay dos lecciones importantes que debería recordar de esta sección.

  1. A diferencia de muchos otros sistemas de control de versiones, las ramas de Subversion existen como directorios normales del sistema de archivos en el repositorio, no en una dimensión extra. Estos directorios simplemente llevan información histórica adicional.

  2. Subversion no tiene un concepto interno de rama—sólo copias. Cuando copia un directorio, el nuevo directorio sólo es una rama porque usted añade esa connotación. Puede considerar el directorio de forma diferente, o tratarlo de manera diferente, pero para Subversion no es más que un directorio ordinario que simplemente fue creado como resultado de una operación de copiado.



[14] Subversion no soporta copias entre diferentes repositorios. Cuando use URLs con svn copy o svn move, sólo puede copiar elementos dentro del mismo repositorio.