Contenido

Combinando Git y Subversion

El otro día mi amigo Diego D. me propuso un problema. Él ya tenía una posible solución, pero quería contar también con mi opinión.

A menudo, cuando tenemos un problema no somos los únicos en enfrentarnos a él. Por eso he decidido publicar aquí mi solución, algo más elaborada de cómo se la conté a él, por supuesto.

Y éste es el problema: Nuestra empresa utiliza Git, y una subcontrata utiliza Subversion. Cada vez que la subcontrata nos tiene que enviar código, lo hace a través del email (ésto no es tan raro, yo también lo he sufrido). Claro, eso supone perder el tiempo colocando los archivos en su sitio y mergeando a mano, con todos los problemas que esto supone.

Veamos mi solución. Si alguien tiene otra mejor, por favor, que lo diga.

Git vs Svn

Primer paso: Ramas

Lo primero que tenemos que hacer es crear una rama en nuestro repositorio Git. Yo la llamaría con el nombre de la empresa externa, algo como “externa/subversion”, por si el día de mañana dejaran la edad de piedra y usaran ellos Git.

Segundo paso: incorporando sus cambios.

Si hay acceso a su repositorio Subversion

Éste es el caso fácil. Basta con seguir este script:

1
2
3
4
5
6
git fetch
git rebase
svn checkout
git add -A
git commit -am "Autocommit"
git push

Es decir: traemos lo que haya de Subversion y lo añadimos a Git. Para evitar conflictos, primero actualizamos la copia local. Aún es posible tener conflictos, pero habremos minimizado las posibilidades.

No hay acceso al repositorio Subversion

Cada vez que nos manden un archivo, nos cambiamos a esa rama, los colocamos y comiteamos. Este proceso es un auténtico coñazo y nos hará perder mogollón de tiempo.

El resto de los puntos serán igual. Sólo estamos sustituyendo el paso svn checkout.

¿Alguien se ha dado cuenta de la gracia del tema? ¡Sí! esto se puede automatizar :D

Automatizando: que se jodan otros

Yo haría una pequeña interfaz web en la que se soliciten los archivos y un pequeño texto explicando lo que contienen. Por detrás, haría el commit que he propuesto anteriormente XD

Si nuestra intención es ser eficientes, la única solución es delegar las tareas que nos hacen perder el tiempo. Si ellos quieren hacernos perder el tiempo, le damos la vuelta. Total, será similar enviar un email que rellenar dicho formulario.

Usando parches

Una mejora es usar parches. De esa manera sólo tendrán que enviarnos un único archivo y lo aplicaremos todo de golpe. En la empresa externa tendrán que hacer:

1
svn diff > changes.patch

Y hacernos llegar el archivo changes.patch. Gracias a este cambio, para nosotros será mucho más sencillo aplicar sus cambios (desde el directorio principal del repositorio):

1
patch -p0 -i /path/al/fichero/changes.patch

Podemos incluir eso en nuestros archivos de automatización.

Tercer paso: incorporando nuestros cambios y haciéndoselos llegar

Si hay acceso a su repositorio Subversion

Nada más sencillo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
old_commit=$(git rev-parse HEAD)
git fetch
git rebase
current_commit=$(git rev-parse HEAD)
if [[ "$old_commit" != "$current_commit" ]]; then
    for line in $(git diff --name-status ${old_commit}..${current_commit}); do
        operation=$(echo line | cut -d\t -f1)
        filename=$(echo line | cut -d\t -f2)
        if [[ "operation" == "D" ]]; then
            svn rm $filename
        else
            svn add $filename || True
        fi
    done
fi
svn commit "Automatic push"

(No he probado el script… pero creo que os podéis hacer a la idea)

No hay acceso al repositorio Subversion

Pues igual, pero aún más sencillo para nosotros:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash

PREFIX="Please, could you apply these changes?\n"
SUFFIX="\n\nThank you."

old_commit=$(git rev-parse HEAD)
git fetch
git rebase
current_commit=$(git rev-parse HEAD)
echo "$PREFIX $(git diff ${old_commit}..${current_commit}) $SUFFIX" \
    | mail -s "Repository changes" empresa@example.org

De la misma manera, se les puede mandar el parche para que lo apliquen ellos.

Añadiendo los cambios

Finalmente, será necesario meter los cambios en nuestra rama principal. Para ello yo seguiría el siguiente esquema:

  1. git fetch
  2. git checkout -t origin/externa/subversion
  3. git rebase rama_principal
  4. Pasar los tests
  5. git checkout master
  6. git rebase external/subversion
  7. Volver a pasar los tests. Aunque este paso es opcional, ya que no debería haber problemas.
  8. git push

Alguien podría decirme: “Es que la empresa externa no escribe tests”. Los que me seguís habitualmente sabéis lo que voy a contestar: ¿Y qué hacéis trabajando aún con ellos? ¿Y es que no tenéis vuestros propios tests de aceptación? ¿Cómo sabéis que no os han roto nada crítico?

Mejoras

Gracias a todo este esquema podéis obtener información extra de forma gratuita: podéis trackear la covertura de los tests, evitando que ésta baje, generar informes, graficar defectos arreglados/producidos por unidad de tiempo, frecuencia de cambios, etc.

Replicabilidad

Otra de las cosas que suelen preocuparme es la replicabilidad de este sistema. Por ello sugiero los siguientes cambios:

  • Añadir archivos subversion: Yo añadiría los archivos más básicos de Subversion al repositorio Git. De esta manera, cualquier working copy es susceptible de realizar los pasos arriba indicados.
  • Scripts en el repositorio: Igualmente, añadiría los scripts que permiten gestionar el repositorio al propio repositorio. Esto permitirá que cualquiera pueda ejecutarlos desde cualquier sitio. Así no serán necesarios incómodos backups del sistema.

Backups

Los que me conocen saben que soy enemigo de los backups. Si estás usando un repositorio Git, todas las working copies son backups potenciales, y restaurar el sistema no debería ser un problema. Bastaría con guardar la configuración del servidor… ¡Que podría estar en otro repositorio! Y si usa Puppet, Salt o Chef, mucho mejor.

Usad los backups para cosas que realmente cambian con frecuencia o no son susceptibles de añadirse a un repositorio (o bien no se va a descargar nadie): Bases de datos, repositorios de archivos (tipo Nexus, Artifactory, …)