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.
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.
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.
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
.
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.
Hay dos lecciones importantes que debería recordar de esta sección.
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.
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.