Migrar un proyecto desde SVN a Bazaar

Hace ya algunos meses estoy utilizando Bazaar como sistema de control de versiones para todos mis proyectos nuevos, con resultados muy satisfactorios: me resulta muchísimo más potente que Subversion (SVN) por su funcionamiento como sistema distribuído, y a la vez más sencillo de usar que git (del que sólo le podría faltar la  velocidad).

Uno de estos proyectos ha sido el rediseño de un sitio bastante grande y complejo que hemos desarrollado en AyerViernes y que hasta ahora se encuentra versionado con svn, pero que queremos trasladar a Bazaar por la buena experiencia que hemos tenido. En este proyecto frecuentemente se realizan cambios al modo de funcionamiento de sus diversas características o se agregan nuevas funcionalidades, por lo que no podíamos trabajar bajo el supuesto de congelar el trabajo en el sitio actual y migrar todo inmediatamente a la nueva versión en desarrollo; en síntesis, debíamos ser capaces de:

  • Seguir implementando cambios en la versión en producción
  • Desarrollar paralelamente la nueva versión, sin interferir con la anterior
  • Poder incorporar los cambios de la versión en producción a la versión en desarrollo

Afortunadamente, Bazaar puede interoperar con Subversion gracias a un plugin llamado (adecuadamente) bzr-svn, disponible en los repositorios de Ubuntu.

La estrategia que utilizamos demuestra la flexibilidad y potencia de Bazaar. Los pasos a seguir serían aproximadamente los siguientes:

Preparación

Si aún no lo tienes, lo primero es instalar el plugin bzr-svn. En Ubuntu esto se puede lograr con sudo apt-get install bzr-svn desde el terminal.

Luego, crear un repositorio compartido bajo el cual almacenaremos tanto la versión online (en producción) como sobre la cual trabajaremos el rediseño (versión de desarrollo), cada una de ellas como una rama (branch).

Al utilizar un repositorio compartido, las revisiones se almacenan en el repositorio en lugar de en cada rama, lo que permite optimizar drásticamente la utilización de espacio y crear nuevas ramas velozmente. Para inicializar el repositorio compartido, debemos ejecutar bzr init-repo proyecto.repo

Importar el historial de versiones

Una vez que está creado el repositorio compartido, nuestro objetivo será importar los archivos y el historial del proyecto desde el repositorio SVN donde se está trabajando actualmente, lo que podemos realizar de diversos modos dependiendo de la forma en que esté organizado el repositorio SVN.

Para ello contamos con dos opciones proporcionadas por el plugin: podemos importar solo una parte del repositorio SVN (por ejemplo, solamente el trunk o una de las ramas) o bien hacer una copia completa del repositorio (incluyendo todas las ramas y etiquetas además del trunk).

Para el primer caso, tenemos la opción de crear una nueva rama a partir de la versión seleccionada tal como si lo estuviésemos haciendo desde una rama de Bazaar: bzr branch svn://servidor.com/proyecto trunk, con lo que Bazaar importará todo el historial de versiones a la nueva rama junto con crear un árbol de trabajo con la última versión de los archivos. Por otra parte, si deseamos que los commits en este árbol siempre se suban al repositorio svn, también contamos con la opción de hacer un checkout (o bien pasar desde una rama a un checkout con bind/unbind).

Para el segundo caso, podemos utilizar el comando bzr svn-import svn://servidor.com/proyecto repositorio. Bazaar importará todo el historial de versiones, pero no creará el árbol de trabajo a menos que agreguemos la opción --trees.

Trabajo en una nueva rama como “feature branch”

Suponiendo que hemos utilizado la primera de las opciones recién indicadas, tendríamos lo siguiente:

$ cd /var/www
# Inicializar el repositorio compartido
$ bzr init-repo proyecto.repo
# Importar el historial del proyecto
$ cd proyecto.repo
$ bzr branch svn://servidor.com/proyecto trunk

Y llegamos finalmente al momento en que comenzaremos a trabajar en la nueva versión. Para ello, crearemos una nueva rama en el repositorio compartido, donde ejecutaremos todos los cambios necesarios para el desarrollo de la nueva versión.

# Crear rama de desarrollo
$ cd /var/www/proyecto.repo
$ bzr branch trunk feature-branch
$ cd feature-branch
# hack, hack, hack...

A partir de este punto, lo que queda es introducir todas las modificaciones que sean necesarias en nuestra nueva versión — dado que es una rama, los commits de cambios serán locales al repositorio. Nuestro flujo de trabajo sería básicamente el siguiente:

De este modo, la mantención del proyecto puede continuar de forma paralela al nuevo desarrollo, sin cruzarse entre sí a menos que así lo queramos. Si hubiesen cambios en el sitio en producción que se envíen al repositorio SVN, podríamos integrarlos dentro de nuestra versión de desarrollo con los siguientes pasos:

# Ingresamos a la rama ligada al sitio en producción
$ cd /var/www/proyecto.repo/trunk
# Obtener los cambios desde el repositorio SVN
$ bzr pull # O bzr up si es un checkout
# Cambiar a la rama en desarrollo
$ cd ../feature-branch
# Incorporar los cambios de producción al nuevo desarrollo
$ bzr merge ../trunk

Del mismo modo, cuando queramos integrar los cambios de la versión en desarrollo a la rama principal, también recurriremos a bzr merge, de modo de preservar el historial que la rama en producción (el “trunk” del ejemplo).

$ cd /var/www/proyecto.repo/trunk
# Mezclar cambios de desarrollo en la versión de producción
$ bzr merge ../feature-branch

Es importante recalcar la utilización de merge, y no un push desde feature-branch al trunk, ya que de otro modo sobreescribiremos el historial de versiones del trunk.

Ya en este punto, podremos chequear que todos los cambios que introdujimos se complementan bien con los últimos cambios del sitio en producción. Una vez que se hayan alisado todas las asperezas, podemos enviar finalmente nuestros cambios desde /var/www/proyecto.repo/trunk con un bzr push hacia el repositorio SVN.

Colaboración con otros desarrolladores

Hasta acá tenemos la versión simplificada, pero lo más probable es que durante el desarrollo debamos cooperar con compañeros de equipo encargados de otras piezas del puzzle… a partir de lo anterior, podemos seguir expandiendo el modelo para incorporar estas nuevas posibilidades: nuestros compañeros pueden crear nuevas ramas locales en sus respectivos equipos, o bien montar un checkout donde implementar cambios que luego serán enviados a nuestro repositorio.

De este modo, una versión más completa del esquema anterior sería la siguiente:

Como se puede ver, existen muchas alternativas para organizar el trabajo en la forma que sea mejor para tu equipo; y lo que es mejor aún, es muy fácil implementar otras variantes que vayan siendo más apropiadas según el desarrollo del proyecto.