Podemos comprometer una aplicación fusionando código problemático, ya sea por integrar accidentalmente trabajo inacabado en la rama principal o por pasar por alto un error crítico que se filtró en las pruebas automatizadas.
En este artículo, te guiaré a través del proceso de usar git revert
para deshacer de manera segura una fusión, asegurando que el historial de commits permanezca intacto y se preserve la integridad del proyecto.
Cómo Funciona git revert
Podemos pensar en git revert
como la versión de Git del comando deshacer. Sin embargo, el comando git revert
no elimina commits ni salta a un estado anterior de la rama. En su lugar, crea un nuevo commit que revertirá los cambios de un commit específico.
La sintaxis para revertir un commit con el hash <commit_hash>
es:
git revert <commit_hash>
Podemos listar los commits junto con sus identificadores de hash usando el comando git log
. La salida de git log
lista los commits de más reciente a más antiguo, así:
Por ejemplo, para revertir el commit que implementa la función de resta, usaríamos el siguiente comando:
git revert 7ba24a3e62d4d37182428ccfaa070baa222b1151
Usando git revert
podemos deshacer los cambios de un commit específico sin afectar el historial de commits.
Tenga en cuenta que git revert
no es mágico, y dependiendo del historial de commits, puede resultar en un conflicto que tiene que ser resuelto manualmente.
Ventajas de git revert
Frente a Cambios Manuales
¿Por qué es útil git revert
si podemos necesitar resolver un conflicto manualmente? ¿No sería más fácil simplemente deshacer los cambios manualmente? Veamos sus ventajas:
- Preserva el historial:
git revert
crea un nuevo commit que deshace los cambios de un commit especificado mientras preserva todo el historial de commits. Esto ayuda a mantener un historial transparente de cambios y reversales. - Reversión atómica: Asegura que las reversaciones sean atómicas y consistentes. Cuando eliminamos y comprometemos cambios manualmente, hay un riesgo de error humano.
- Conciencia de conflictos: Asegura que seamos alertados a través de conflictos si hay integraciones o cambios dependientes del commit original. Esto puede parecer inconveniente, pero protege contra efectos secundarios no deseados.
- Metadatos: El nuevo commit creado por git revert incluye metadatos y un mensaje de commit que describe contextualmente lo que fue revertido, ayudando en la comprensión futura. Sin
git revert
, este contexto podría perderse.
Revertir una Fusión en Diferentes Escenarios
En esta sección, aprendemos cómo deshacer una fusión. A modo de ejemplo, asumimos que estamos fusionando una rama llamada feature
en la rama main
ejecutando el comando desde la rama main
:
git merge feature
Lo que aprendamos aquí puede aplicarse a cualquier par de ramas reemplazando los nombres appropriately.
Revertir una fusión que no tiene un commit asociado
El comando git merge
no siempre crea un nuevo commit. Un commit se crea solo si la rama main
se ha desviado de la rama feature
. Porque git revert
requiere un commit para operar, no podemos usarlo en este caso.
Las ramas main
y feature
se desvían cuando se crean nuevos commits en main
que no son ancestros de la rama feature
. En otras palabras, se crearon nuevos commits en main
después de que se creó feature
.
Si las ramas no se han desviado, al ejecutar el comando git merge feature
en la rama principal, Git utilizará un fast-forward para fusionar. Esto significa que mueve el HEAD
de la rama main
al HEAD
de la rama feature
.
Podemos observar que esto ocurrió mirando el resultado del git merge
:
Para deshacer tal fusión, solo necesitamos mover el HEAD
de la rama main
a donde estaba. Para esto, nosotros:
- Identificamos el
HEAD
anterior usando elgit reflog
- Restablece el
HEAD
al anterior usandogit reset --hard <previous_head>
, reemplazando<previous_head>
con elHEAD
anterior.
La salida de git reflog
se verá algo como esto:
Podemos identificar el HEAD
anterior mirando la línea que dice “checkout: moving from feature to main” (escribe feature
y main
porque esos son los nombres de nuestras ramas).
En este caso, el HEAD
anterior es fe59838
. Para mover el HEAD
de la rama principal hacia atrás y deshacer la fusión, usamos el comando:
git reset --hard fe59838
Revertir una fusión que tiene un commit asociado
Si las ramas main
y feature
han divergido, entonces al fusionar dos ramas, se crea un nuevo commit, llamado commit de fusión.
El commit de fusión aplica los cambios de una rama a otra. En este caso, los cambios en feature
se aplican a la rama main
.
Para revertir los cambios en la rama main
, usamos git revert
en el commit de fusión. Esto creará un nuevo commit que deshace los cambios introducidos en la rama main
con la fusión, restaurando efectivamente el estado de la rama principal al que tenía antes de la fusión.
Primero, necesitamos identificar el hash del commit de fusión. Podemos hacer esto utilizando el comando git log
:
Debido a que el commit de fusión tiene dos padres, la sintaxis de git revert
es ligeramente diferente. Necesitamos usar la opción -m 1
para especificar que queremos revertir los cambios relativos a la rama main
:
git revert -m 1 b8dab2c8611e324ed0d273133987415350e6d10d
Resolución de Conflictos al Revertir un Commit
En algunos casos, pueden surgir conflictos al revertir un commit, especialmente si el commit que se revierte entra en conflicto con cambios posteriores en el código base. En tales casos:
- Git pausará la reversión: Necesitamos resolver los conflictos manualmente. Git marcará los archivos en conflicto y requerirá intervención.
- Resolver los conflictos: Abrimos cada archivo en conflicto, resolvemos los conflictos marcados por Git, y guardamos los cambios.
- Stage los archivos resueltos:
git add <ruta-del-archivo>
- Continuar con la reversión:
git revert --continue
Conclusión
Usar git revert
para deshacer commits de fusión asegura que cada cambio y corrección esté documentado dentro del historial de commits.
Además, entender los escenarios adecuados para aplicar git reset
frente a git revert
nos permite tomar mejores decisiones, especialmente cuando se considera flujos de trabajo colaborativos o cambios locales exclusivamente.
Puedes leer más sobre este tema en la sección de preguntas frecuentes a continuación. Si deseas aprender más sobre Git, te recomiendo estos recursos: