Forzando los merge commits

Una de las preguntas más habituales que se hacen en los cursos de git que imparto regularmente, y que también se me ha planteado en algunas de las reuniones de desarrolladores en las que participo es la siguiente: ¿Cómo evito un fast-forward para dejar constancia en el repositorio de dónde empieza y termina una rama?

Forzar merge commits

Lo más claro, como siempre, es verlo con un ejemplo. Imaginad que llegáis por la mañana al trabajo y empezáis como siempre a picar código como locos en la rama devel. De repente surge la necesidad de atender un bug. Siguiendo las buenas prácticas, lo que haremos será crear una rama HotFix, arreglar el problema haciendo cuantos commits necesitemos a esa rama y finalmente hacer un merge a la rama que corresponda cuando terminemos de corregir el bug.

En la siguiente captura se muestra el estado del repositorio justo después de haber hecho el último commit a la rama hotfix_14523 que arregla el problema en cuestión:

Estado del repositorio al terminar el hotfix 15423

Si en este momento se hace un merge de la rama hotfix_15423 a la rama devel, se hará un fast-forward:

$ git checkout devel
$ git merge hotfix_15423
Updating 2d1d1c4..2b9d126
Fast-forward
Gemfile | 2 ++
app/views/home/.erb | 1 +
2 files changed, 3 insertions(+)

Así queda el grafo después de hacer el merge:Estado del repositorio tras hacer un merge con fast-forward de la rama hotfix_15423

Como vemos, mirando la historia del repositorio, no queda constancia de cuándo se empezó el desarrollo de la rama hotfix_15423 y cuándo se incorporó a la rama devel. Una solución podría ser etiquetar los commits correspondientes, como se muestra en la siguiente captura:Usando etiquetas para indicar el inicio y final de una rama

Esta solución es válida cuando tenemos pocas ramas. Si tenemos muchas ramas de las que queramos saber su inicio y final, podemos acabar con un número de etiquetas difícil de gestionar.

Forzar un merge commit

Volvamos a la situación inicial:

$ git reset --hard 2d1d1c4
HEAD is now at 2d1d1c4 Update README.rdoc

Estado del repositorio al terminar el hotfix 15423

En esta situación, volvemos a ejecutar el merge de la siguiente forma:

$ git merge --no-ff hotfix_15423 -m'Incorporando rama hotfix_15423'
Merge made by the 'recursive' strategy.
 Gemfile | 2 ++
 app/views/home/.erb | 1 +
 2 files changed, 3 insertions(+)

Después de ejecutar el comando, este es el estado del repositorio:

Incoporando hotfix15423 en devel con la opción --no-ff

Haciéndolo de esta forma, en lugar de hacerse un fast-forward, vemos que se crea un merge-commit, lo que resulta en una bifurcación que indica claramente el fin y el inicio de la rama hotfix_15423.

Si sois de los que os gusta ver claramente dónde empiezan y terminan vuestras ramas, este pequeño truco es resultará de utilidad.

4 comentarios en “Forzando los merge commits

  1. Pingback: git-flow: la rama develop y uso de feature branches | Aprende GIT

  2. Jose

    Hola, Muy interesante el articulo y mu util, queria preguntar algo:

    ¿El merge con fast-fortward es lo mismo que el rebase?
    ¿Utilizais siempre merge apra aplicar cambios?

    gracias, Un saludo

    1. alfonso

      Hola Jose:

      Gracias por el comentario. Respondiendo a tus preguntas:

      • No, un fast-forward no es lo mismo que un rebase, aunque a veces el resultado sea el mismo lo que hace internamente git con los commits no tiene nada que ver.
      • No, no siempre usamos merge para aplicar cambios, utilizamos merge, rebase y a veces pick y cherry-pick

      Un merge con fast-forward se produce cuando puedes moverte de una rama a otra por el grapho de objetos en una única dirección, es decir, si puedes ir de una rama a otra sólo bajando o subiendo. Si tienes que subir y luego bajar (o viceversa) necesitas un merge commit para poder incorporar los cambios de una rama en la otra.

      Con respecto al rebase, te pongo un ejemplo real. En un proyecto que tengo ahora tengo tres ramas de pruebas en las que estoy probando cosas. Por otro lado, tengo la rama development en la que voy desarrollando el proyecto. Cada pocos días, incorporo las modificaciones de «development» en las ramas de prueba para estar al día. Esto lo hago con un rebase en lugar de un merge. ¿El motivo? me ahorro un montón de merge commits que no me aportan nada y me enguarrinan la historia del repositorio. Como son mías y no las usa nadie (están en mi remoto pero nadie las utiliza…y si lo hace ya sabe lo que se va a encontrar) no hay problema.

      Por otro lado, cuando hago un hotfix en la rama «production», me lo llevo a a la rama develop usando un merge. Si lo hiciese con rebase, estaría modificando los commits. Esto supone un problema ya que estas dos ramas están compartidas y al borrar unos commits y cambiarlos por otros con el rebase volvería locos a mis compañeros.

      ¿Te ha aclarado algo la respuesta?

      Un saludo,

      Alfonso

      1. Jose

        Hola Alfonso,

        Muchas gracias por la información me he aclarado mucho mas, yo normalmente utilizo merge para ramas production (compartidas) y rebase para las ramas de desarrollo.

        gracias, Un saludo

Los comentarios están cerrados.