martes, 18 de marzo de 2014

GIT: workflow, merge/rebase de ramas remotas.

Hay una gran variedad de workflows distintos para el trabajo con GIT. Dependiendo del tipo de equipo conviene adaptarse a uno u a otro. El más usado posiblemente sea git-flow, donde tenemos una rama master donde se integran los cambios, para pasar una vez validados a la rama stable.

La bonita utopía 

En este workflow, cada desarrollador tiene su copia local de master, desde donde creará ramas locales para implementar las funcionalidades del proyecto

git checkout -b mitarea

Cada vez que finalice la funcionalidad se integrará con la rama master y los cambios que haya podido sufrir durante el desarrollo. Lo ideal sería algo así:
git checkout master
git pull origin master
git checkout mitarea
git rebase master
git rebase master -i
git checkout master
git merge mitarea
git push origin master

Con esto nos aseguramos de que nuestro master este actualizado; hacemos rebase contra nuestra rama local, de modo que añadiremos los commit que hemos realizado. Luego haremos el rebase interactivo (opcional) para que todos nuestros commit se integren en uno, y la rama estará lista para que sea subida a master.



Complicando la cosa

Hay ocasiones en el que en la funcionalidad que se desea es necesario que trabajen varios desarrolladores. Para esto, se creará una rama remota a partir de master, subiéndola al origen:

git checkout -b tarea-tocha
git push origin tarea-tocha

Por tanto, ahora tendremos un subequipo trabajando con esta otra rama. Si pasa demasiado tiempo, posiblemente queramos que esta rama esté algo más actualizada, y necesitemos para ello incluir los cambios de master. En este caso, NO se debe hacer un rebase.

No se hace un rebase si los cambios han sido subidos a una rama pública remota


Creo que queda claro, no es difícil encontrar esta premisa si se busca un poco. Lo que se debe hacer en este caso es merge. Simplemente:

git checkout master
git pull origin master
git checkout tarea-tocha
git pull origin tarea-tocha
git merge master

Es posible que surja algún conflicto al hacer esto, pero si se lleva hace con algo de frecuencia (depende del movimiento que haya en el repositorio) será una tarea muy fácil. Y cuando se de por terminado el desarrollo en esta rama, con hacer la operación inversa, es decir, un merge de la rama a master, se habrán integrado todos los cambios.

¿Qué pasa si en lugar de esto, hacemos un rebase?

Un error típico, o por lo menos que veo más a menudo de lo que debería, es realizar un rebase cuando se debería haber hecho un merge. Esto provoca que se dupliquen commits, que dará lugar a conflictos, código repetido y desesperación.
Lo mejor que puede pasar es que la persona que lo haga se de cuenta a tiempo y no haga push. Si es así, basta con usar el comando git reflog donde veremos las últimas acciones que hemos hecho con git (una especie de historial). Buscaremos el último comando commit válido realizado (nada de rebase o merge), por ejemplo, el HEAD 32 y hacemos un

git reset --hard HEAD{@32}

Esto hará que nuestro repositorio local se coloque en el estado que teníamos antes del rebase, por lo que podremos hacer el merge (esta vez sí) en condiciones.

Si el sujeto ha hecho push .... en fin. Es muy probable que haya que hacer, después del reset, un push --force, lo que "eliminaría" el repo remoto y pondría en su lugar el local... nada recomendable, la verdad.