Artículo

Comandos de Git: Rebase

Tech & innovation

Comandos de Git: Rebase

Seguimos con nuestra serie de posts sobre comandos de git menos comunes.

En este post os presentamos el rebase. Si os perdisteis el primero no dudéis en leer Seleccionando commits: Cherry-pick.  

Trabajando con Git es bastante común encontrarse con conflictos a la hora de hacer merge, por lo tanto, ya sabréis como funciona la resolución de los mismos. Al resolverlos os habréis dado cuenta que en el commit de merge figura el código que queríais integrar a la rama y la resolución del conflicto.

¿No sería más limpio formular nuestras acciones de forma que no generen conflictos, y su resolución quede reflejada en el historial?

Si habéis trabajado en un proyecto grande, ya sea de código abierto o no, agradecisteis que los commits fueran precisos y auto-contenidos. Esto nos permite entender exactamente cuáles de ellos implementan una funcionalidad concreta. ¿Acaso creéis que siempre se desarrollan esas funcionalidades de una sola tirada y sin commits intermedios?

A no ser que seas un crack, tengas mucha suerte o la funcionalidad que trates de implementar sea muy pequeña, probablemente no lo consigas hacer de buenas a primeras. Es posible que en tu primer intento te queden commits parciales, de sesiones de trabajo sin finalizar, commits de cosas que no funcionan, o reverts de cosas que has hecho anteriormente.

¿Cómo mantenemos todo este “ruido” fuera de la historia?  

Existe un comando de Git que puede ayudarnos a mantener la historia pública del proyecto limpia: Git rebase.

Entremos más en detalle sobre este comando de Git. Git rebase nos permite modificar la historia de Git utilizando un punto de referencia, es decir, el hash de un commit en concreto. Si le damos un punto de partida (la rama actual en la que estamos trabajando) y un punto de referencia (puede ser una rama o directamente el hash de un commit), Git es capaz de reconocer que commits no están en la línea temporal de ese punto de referencia y de aplicarlos encima del punto de referencia.

Esto implica que se están volviendo a crear esos commits con el mismo mensaje y el mismo contenido, lo cual puede dar lugar a conflictos a la hora de aplicarlos.

Si esto pasa, se tienen que ir resolviendo a medida que van ocurriendo y se consideran modificaciones del propio commit (sin utilizar commits de merge). Además de esto, podremos modificar los commits antes de aplicarlos.

Git nos provee con unos mecanismos ya definidos que nos permitirán borrar, reordenar, unificar o cambiar el mensaje de commit, pero si no son suficientes siempre podremos editar directamente el commit antes de aplicarlo.

Una de las aplicaciones más interesantes de este comando es la unificación de los commits, también conocida como squash la cual nos permite decirle a Git que 2 o más commits se acaben juntando en uno solo (modificando, o no, el mensaje de commit). Hasta aquí todo bien, pero es muy teórico, pero ¿cómo lo aplico en mi día a día o qué ideas puedo sacar de aquí?

Supongamos que llevas una semana trabajando en una funcionalidad y otro compañero tuyo está trabajando en otra funcionalidad distinta, sobre el mismo código. Como estamos utilizando Git, cada uno estará en su propia rama de feature/, ¿verdad? Aprovechamos para recomendaros Git flow, no la herramienta, pero sí el concepto. En algún momento, uno de los dos completará su trabajo y hará merge a development. Entonces nosotros tenemos que actualizarnos nuestro trabajo con los nuevos cambios que hay en development. Podemos hacerlo con el clásico merge o podemos utilizar el rebase. git fetch --all git rebase -i origin/development  

Esto nos va a mostrar los commits en los que hemos trabajado estos días y que aún no están en development.

Ahora vamos a poder hacer squash de ciertos commits que se nos han colado, que simplemente arreglan problemas de commits anteriores.

Podremos re-ordenar si es necesario para ofrecer mayor claridad a la persona que tenga que revisar el histórico de Git, y por último podremos borrar algunos commits que hemos revertido (hacemos algo, y luego nos damos cuenta de que no era necesario).

Una vez estamos contentos con lo que queremos aplicar guardamos el fichero y lo cerramos. Nuestro log quedaría finalmente de la siguiente manera.

Podemos ver como se han fusionado los dos primeros commits y como el tercero y su revert han desaparecido.

Git va a aplicar todos esos cambios recreando los commits encima de origin/development.

Si se encuentra con algún conflicto lo notificará y funcionará exactamente igual que los conflictos durante el merge.

Con la excepción que no va a crear ningún commit de merge.

Cuando los tengamos arreglados habrá que pedir al comando que continue mediante: git rebase --continue

Una vez ha acabado solo tenemos que forzar un push, debido a que la historia ha cambiado. git push origin feature/ --force  

Recomendaciones:

  • Cuidado con modificar la historia cuando están trabajando mas de una persona en esa rama. Nosotros no lo recomendamos.
  • Si estás trabajando solo en ella todo bien.
  • Si estás trabajando usando pull request, todo bien.

 

Este artículo es parte de la serie sobre comandos de Git y está escrito por Jordi Sala y Marc Mauri. Esperamos que os sea útil. 

21 Jul. 2017

Equipo Developers

Runroomer

¡Hablemos!