Git es un sistema de control de versiones open source, totalmente gratuito. Fue diseñado por Linus Torvalds con el objetivo de posibilitar la gestión eficiente y veloz de cualquier tipo de proyecto de software. Actualmente es utilizado para gestionar el código fuente de algunos de los proyectos open source más importantes entre los que se destacan: Linux Kernel, Debian, Gnome, Ruby on Rails, Android, entre otros.
En INSIGNIA adoptamos Git como sistema de control de versiones para todos nuestros proyectos porque en él encontramos una herramienta con un sin fin de funcionalidades que podían ser adaptadas y utilizadas sin ningún problema a nuestro workflow. Nos fascinó la facilidad con la que se pueden crear branches, realizar merge de cambios de un branch a otro y mantener todo bien sincronizado.
Luego de 2 años de utilizarlo -y exprimirlo- para gestionar nuestros proyectos, hemos encontrado muchos detalles de esta herramienta que pueden afectar negativamente la productividad del equipo sino se aplican algunas buenas prácticas y costumbres para mantener la casa en orden.
A continuación, presentamos el workflow que aplicamos en nuestra activadad diaria para utilizar Git desde un enfoque ágil y productivo.
Primero repasemos cómo es el workflow básico propuesto para Git. Para esto clonaremos uno de nuestros proyectos open source, el primer comando que deberemos ejecutar es:
# git clone git://github.com/insignia/administrate_me.git
Esto nos descargará la última versión del proyecto en el directorio administrate_me.
Si queremos ver cuáles fueron los últimos cambios que se introdujeron en el proyecto, podemos ejecutar:
# git log
Y obtendremos un listado cronológico con todos los cambios que se efectuaron en el código del proyecto.

Ahora si modificamos un archivo (por ejemplo el README del proyecto), y ejecutamos:
# git status
Podremos ver qué archivos se encuentran modificados. El resultado sería algo así:

El output del comando git status, nos aporta un dato muy importante: el branch donde estamos trabajando. Como mínimo, siempre tendremos el branch master.
Para agregar nuestros cambios, debemos hacer un commit de los mismos, eso se logra ejecutando:
# git commit -a -m "Modifico el archivo README"
Finalmente, debemos subir esos cambios al repo central (conocido técnicamente como origin), pero antes tendremos que revisar que nuestro código se encuentre actualizado con este repo, para esto:
# git pull # git push
Y este sería el workflow más simple que se podría dar en Git.
La práctica más recomendada para trabajar en Git indica que todo nuestro trabajo debería ser realizado en un branch temporal y cuando estos cambios están listos, hacer un merge de esos cambios en master y su posterior push.
Para crear un branch, simplemente ejecutamos los siguientes comandos:
# git branch mi-nuevo-branch # git checkout mi-nuevo-branch
O podemos ejecutar todo en único comando con:
# git checkout -b mi-nuevo-branch
Luego, una vez que nuestros cambios están listos, los pasamos al branch master ejecutando:
# git checkout master # git merge mi-nuevo-branch
O si necesitamos subir un cambio en particular, podemos pasar un commit específico de un branch a otro, ejecutando:
# git checkout master # git cherry-pick 9c108bfab98afe92b03fa15edb926bc3374ce8f5
Donde 9c108bfab98afe92b03fa15edb926bc3374ce8f5 es el hash que identifica a un commit en particular.
Esta manera de utilizar Git sigue siendo simple y ofrece muchísimas bondades. El mayor beneficio es el poder crear un branch que contenga la versión estable del proyecto (nosotros normalmente nombramos a este branch como production), y en él se van agregando todos los cambios aprobados por medio de la técnica de cherry-picking.
De ser necesario, Git nos proporciona las herramientas para identificar qué commits todavía no fueron incluídos en un branch determinado (por ejemplo: en production). Para esto, tenemos que ejecutar el siguiente comando:
# git checkout production # git log --reverse --cherry-pick master...
Utilizamos la bandera –reverse para saber qué commit debería ser incluído primero y con la bandera –cherry-pick master… indicamos que solamente queremos ver aquellos que se encuentren en el branch master que todavía no se encuentren en production.
Utilizando este enfoque durante un tiempo, nos encontramos con que el listado de cambios pendientes que nos retornaba Git no era del todo preciso. Al investigar las posibles causas, encontramos que Git falla al determinar los commits pendientes en aquellos casos donde se hace complicado establecer qué commit precede a otro.

En este caso podemos ver que los commits no se encuentran alineados cronológicamente sino que en algunos casos, algunos commits son precedidos por dos o mas commits. Es en estos casos donde la técnica del cherry-picking nos puede arrojar resultados o comportamientos extraños.
Este es un problema que se hace demasiado notorio cuando los proyectos donde intervienen grandes equipos. De nuestra experiencia en el desarrollo de la nueva versión de Burdastyle.com (donde en el proyecto participaban equipos distribuidos entre Tucumán, Manchester y New York), encontramos que a menos que todos los miembros del equipo apliquen una política para el manejo de Git dentro del proyecto.
Luego de examinar un poco las distintas experiencias con Git de otros equipos, encontramos que el principal problema para este comportamiento incorrecto de Git era causado por la técnica utilizada para sincronizar los branches. Básicamente la técnica de pull/merge no tiene en cuenta el orden cronológico de los commits al mover commits de un branch a otro. Para solucionar esto, encontramos que también se podía sincronizar los branches por medio de la técnica fetch/rebase, de esta manera nuestros commits son pasados de un branch a otro pero teniendo en cuenta el orden cronológico de los mismos.
Utilizando esta técnica para realizar el paso de commits de un branch temporal a master, deberíamos ejecutar:
# git checkout mi-nuevo-branch # git rebase master # git checkout master # git merge mi-nuevo-branch
Y si necesitaramos sincronizar nuestro branch master con origin, podríamos hacerlo de la siguiente manera:
# git fetch # git rebase origin/master
De esta manera logramos preservar el orden cronológico de todos los commits.

Con todo esto, el workflow ideal para trabajar con Git estaría conformado por los siguientes pasos:
- Realizar todo el trabajo en un brach temporal.
- Antes de pasar los cambios a master, ejecutar un rebase de dicho branch con master.
- Hacer merge de los cambios en master.
- Sincronizar nuestro master con origin/master utilizando fetch/rebase.
- Subir nuestros cambios.
- Si tenemos cambios que pasar a un branch estable, hacerlo con el comando cherry-pick.
Para profundizar sobre Git y sus buenas prácticas, algunos links interesantes para leer son:
El resto es práctica y mucha predisposición para solucionar de la mejor manera cualquier inconveniente con el que nos encontremos.





