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:
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:
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:
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
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:
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.
Pingback: git-flow: la rama develop y uso de feature branches | Aprende GIT
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
Hola Jose:
Gracias por el comentario. Respondiendo a tus preguntas:
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
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