Manual para el uso de branches y merges con subversion

RECU-0326 (Recurso Manual)

Descripción

El uso de branch puede tener distintas estrategias. En este manual explicaremos cuál es la forma recomendada y la aplicación al entorno de recepción de entregas de una Consejería u Organismo.

Características

Empleo de un branch

Un branch es una linea de desarrollo distinta de la principal. Generalmente los desarrolladores trabajan sobre el trunk del proyecto, pero en ciertas ocasiones puede ser útil crear una linea de desarrollo paralela, para esto se usa el término branch.

Existen distintos casos en los que se puede aplicar el uso de branches en un sistema de control de versiones, como por ejemplo: querer comenzar el desarrollo de un nuevo producto a partir de una versión del que tenemos para darle una orientación diferente, para abordar un trabajo de mantenimiento u integración, para atender peticiones de mejora o tareas concretas todo esto sobre una versión que se consiguió en un momento concreto del desarrollo y que se marco con un tag.

Para entender el uso de un branch nos centraremos en el caso en que interese modificar una versión que ya existe en producción sin que pasen de momento otras tantas que se han generado en el sistema de control de versiones después. Imaginaremos que tenemos un proyecto con una linea de desarrollo principal (sobre el trunk), y que en ciertos momentos algunas de las versiones (tags) que se marcaron pasaron al entorno de producción. Pero en un determinado momento se detecta un error critico o una tarea (por ejemplo cambiar la integración con otra aplicación) y hay que abordarla de forma rápida sobre la versión que hay en producción y si pasar toda la nueva funcionalidad de las versiones que la siguieron y que no han pasado por un proceso de pruebas.

En este caso el equipo que desarrolla el proyecto debe de crear un branch sobre el tag que marca la versión que hay en producción y sobre la que se quiere implementar alguna tarea concreta. Por ejemplo en el siguiente diagrama podemos ver que sobre la versión 1.0.1 se crea un branch 1.0.1b.

Una vez finalizada la tarea tendremos una versión que podremos desplegar (pasando por el procedimiento de entrega y pruebas previamente) en producción.

Para evitar que se repitan tareas en las distintas lineas, cada branch debe representar el desarrollo una tarea concreta para que se vuelva a integrar en la linea principal de desarrollo en poco tiempo, de esta manera la tarea de integración sera menos costosa.

En la imagen anterior podemos ver como el branch se integra sobre la linea principal. En el caso de un repositorio de control de versiones que solo se use para la entrega del software en una Consejería u Organismo no es necesario repetir este trabajo de fusión ya que es el equipo de desarrollo sobre su sistema de control de versiones el que realiza este merge. En el caso del este repositorio de código para entregas como se indica en el Procedimiento de entrega del software lo que si es obligatorio es cuando se realice la entrega se actualice y se cree el branch en el Subversion. Podemos ver en la imagen siguiente como quedaría el Subversion donde se guardan las entregas de software.

Ejemplos

Comandos empleados

Para ilustrar el caso expuesto en el apartado anterior veamos el siguiente ejemplo.

Podemos ver en la imagen que tenemos un repositorio con un proyecto y distintos tags

Supongamos que la última versión en producción es la 1.6.0 y que se ha requerido una corrección urgente. Vamos a crear un branch para realizar la tarea crítica A sobre la versión de producción la 1.6.0.

svn mkdir -message='creamos carpeta branch 1.6.0b' 
http://localhost/svn/crija/branches/1.6.0b

svn copy -message='branch 1.6.0b para tarea A'
http://localhost/svn/crija/tags/1.6.0/crija/
http://localhost/svn/crija/branches/1.6.0b/crija

Para realizar el ejemplo creamos una copia local sincronizada de esta nueva linea de desarrollo.

svn co http://localhost/svn/crija/branches/1.6.0b crija_1.0.6b

Vamos a tocar un único fichero para el ejemplo. Vamos a añadir un comentario al pom.xml del proyecto donde ponemos que la tarea A se ha resuelto y ejecutamos commit en la copia de trabajo que tenemos del branch.

svn commit

En este momento tenemos un branch en el que se ha realizado cambios en un único fichero, pero en la linea de desarrollo normal, trunk, también hemos ido evolucionando este fichero con comentarios y marcando varios tags.

A continuación tenemos la tarea de integrar los cambios de nuestro branch en la linea principal de desarrollo, para ello analizamos con el comando svn log en que punto nos separamos de ella cuando creamos el branch.

svn log --stop-on-copy

------------------------------------------------------------------------
r20 | admin | 2008-04-03 09:35:52 +0200 (Thu, 03 Apr 2008) | 1 line

essage=branch 1.6.0b
------------------------------------------------------------------------

Vemos que fue sobre la en la release 20. Para realizar el merge necesitamos tener una carpeta con una copia local actualizada que apunte al trunk del proyecto. Si ya la tuvieramos hariamos un update sobre esta, para el ejemplo la creamos.

svn co http://localhost/svn/crija/trunk/crija/ crija

Ejecutamos el comando svn merje con la opcción --dry-run sobre la copia local sincronizada con trunk. La opción --dry-run que hace que solo nos muestre lo que haría el merge sin hacer los cambios realmente.

svn merge --dry-run 
-r 20:HEAD http://localhost/svn/crija/branches/1.6.0b/crija
C    pom.xml

En el código podemos ver que se indica la release en la que nos separamos y la ruta del branch que queremos unir a nuestra copia local del trunk. Vemos que ha habido cambios en el fichero pom.xml y que tenemos conflictos entre nuestros cambios en el branch 1.6.0b y los que se hayan hecho sobre trunk. Vamos a intentar ejecutar el merge.

svn merge -r 20:HEAD http://localhost/svn/crija/branches/1.6.0b/crija
C    pom.xml

Esto realizara los cambios sobre la copia de trabajo en trunk, no sobre el servidor. Como subversión detecto conflictos al ejecutar merge creara en el fichero modificado unas lineas indicando donde haya encontrado los conflictos. En el ejemplo podemos ver el comentario que añadimos en nuestro branch indicando que se realiza la tarea A y otro que ha sido añadido en versiones posteriores.

<<<<<<< .working
<!-- cambio para la versión 1.6.2 -->
=======
  <!-- tarea A arreglada -->
>>>>>>> .merge-right.r21

Resolvemos el conflicto dejando en este caso ambos cambios y comprobaremos que la aplicación se compila, se empaqueta y funciona correctamente. Finalmente haremos el commit para integrar los cambios del branch 1.6.0b en la rama de trunk.

svn commit