Reescritura del historial
git commit --amend y otros métodos para reescribir el historial
Introducción
En este tutorial se explicarán varios métodos para reescribir y alterar el historial de Git. Git usa distintos métodos para registrar los cambios. Se comentarán las ventajas e inconvenientes de cada uno y se darán ejemplos sobre cómo trabajar con ellos. El tutorial abarca algunas de las razones más habituales para sobrescribir instantáneas confirmadas y muestra cómo evitar los peligros de hacer tal cosa.
El trabajo principal de Git es garantizar que nunca pierdas un cambio confirmado. Pero también está diseñado para otorgarte un control total sobre tu workflow de desarrollo. Por tanto, te permite definir exactamente el aspecto de tu proyecto; sin embargo, también crea el potencial de perder commits. Git proporciona sus comandos de reescritura del historial sin hacerse responsable del contenido que se pueda perder al usarlos.
Git cuenta con varios mecanismos para almacenar el historial y guardar los cambios: commit --amend
, git rebase
y git reflog
. Estos comandos te proporcionan potentes opciones de personalización del workflow. Al terminar este tutorial, te habrás familiarizado con los comandos que te permiten reestructurar los commits de Git y sabrás cómo evitar los peligros habituales de reescribir el historial.
Material relacionado
Chuleta de Git
VER LA SOLUCIÓN
Aprende a usar Git con Bitbucket Cloud
Modificación del último commit: git commit --amend
El comando git commit --amend
es una manera práctica de modificar el commit más reciente. Te permite combinar los cambios preparados con el commit anterior en lugar de crear un commit nuevo. También puede usarse para editar el anterior mensaje del commit sin cambiar la instantánea. Sin embargo, el comando no se limita a alterar el commit más reciente, sino que lo reemplaza por completo, por lo que el commit corregido será una entidad nueva con su propia referencia. Para Git, se verá como un commit nuevo, el cual se distingue con un asterisco (*) en el siguiente diagrama. Hay unos cuantos casos habituales en los que se usa el comando git commit --amend
; los cubriremos en los apartados siguientes.
Modificación del mensaje del commit de Git más reciente
git commit --amend
Digamos que acabas de realizar un commmit y te das cuenta de que hay un error en el mensaje de registro. Ejecutar este comando cuando no hay nada preparado te permite editar el mensaje del commit sin alterar su instantánea.
Los commits prematuros ocurren cada dos por tres durante el curso del desarrollo. Es fácil olvidarse de preparar un archivo o dar el formato incorrecto al mensaje del commit. La marca --amend
es una manera cómoda de arreglar estos pequeños fallos.
git commit --amend -m "an updated commit message"
Añadir la opción -m
te permite escribir un nuevo mensaje desde la línea de comandos sin tener que abrir un editor.
Modificación de archivos confirmados
El ejemplo siguiente muestra una situación habitual en el desarrollo basado en Git. Supongamos que hemos editado algunos archivos que queremos confirmar en una sola instantánea, pero que nos olvidamos de añadir uno. Arreglar este error tan solo es cuestión de preparar el otro archivo y confirmar con la marca --amend
:
# Edit hello.py and main.py
git add hello.py
git commit
# Realize you forgot to add the changes from main.py
git add main.py
git commit --amend --no-edit
La marca --no-edit
te permite hacer las correcciones en el commit sin cambiar el mensaje. El commit resultante reemplazará al incompleto y parecerá que hemos confirmado los cambios a hello.py
y main.py
en una sola instantánea.
No modifiques commits públicos
Los commits corregidos son, de hecho, commits totalmente nuevos y el commit anterior ya no estará disponible en tu rama actual. Esto tiene las mismas consecuencias que restablecer una instantánea pública. Procura no corregir un commit en el que otros desarrolladores hayan basado su trabajo, ya que crea una situación confusa para ellos de la cual es complicado recuperarse.
Resumen
Repasemos: git commit --amend
te permite añadir los nuevos cambios preparados al commit más reciente. Puedes añadir o eliminar cambios del área de preparación de Git con un commit --amend
. Si no hay ningún cambio preparado, --amend
seguirá pidiéndote que modifiques el mensaje de registro del último commit. Ten cuidado al usar --amend
en commits compartidos con otros miembros del equipo. Puede que modificar un commit compartido con otro usuario conlleve confusas y largas soluciones del conflicto de fusión.
Modificar commits más antiguos o varios commits al mismo tiempo
Si quieres modificar commits más antiguos o varios commits a la vez, puedes usar git rebase
para combinar una secuencia de commits en un nuevo commit base. En el modo estándar, git rebase
te permite literalmente reescribir el historial, aplicando de forma automática los commits de tu rama de trabajo actual a la rama head anterior. Dado que los nuevos commits reemplazarán a los antiguos, es importante que no uses git rebase
en commits que se han hecho públicos, o parecerá que el historial de tu proyecto ha desaparecido.
En estos casos u otros similares en los que sea importante mantener limpio el historial del proyecto, añadir la opción -i
al comando git rebase
te permitirá ejecutar rebase interactive
(reorganización interactiva). Esta opción te da la oportunidad de alterar commits individuales durante el proceso, en lugar de mover todos los commits. Puedes aprender más sobre la reorganización interactiva y conocer comandos de reorganización adicionales en la página sobre git rebase.
Modificación de archivos confirmados
Durante esta operación, el comando de edición o e
detendrá la reorganización en ese commit y te permitirá hacer cambios adicionales con git commit --amend
. Git interrumpirá la reproducción y mostrará el siguiente mensaje:
Stopped at 5d025d1... formatting
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Varios mensajes
Cada commit de Git contará con un mensaje de registro que explique lo ocurrido en el commit. Estos mensajes proporcionan información valiosa sobre el historial del proyecto. Durante la reorganización, puedes ejecutar distintos comandos en los commits para modificar esos mensajes.
Combinación de commits para un historial limpio
El comando s
nos permite ver la verdadera utilidad de la reorganización. Este comando de combinación te permite especificar qué commits quieres fusionar con los commits anteriores. Así, el historial se mantendrá limpio. Durante la reorganización, Git ejecutará los comandos especificados para cada commit. En el caso de los commits squash, Git abrirá el editor de texto configurado y te pedirá que combines los mensajes de commit especificados. Este proceso se puede representar de la siguiente forma:
Ten en cuenta que los commits modificados con el comando rebase tienen un ID distinto del de los commits originales. Los commits marcados con pick tendrán un nuevo ID si los commits anteriores se han reescrito.
Las soluciones de almacenamiento de Git modernas, como Bitbucket, ahora ofrecen funcionalidades de "autocombinación" al fusionar. Estas funcionalidades llevarán a cabo una reorganización y combinarán los commits de una rama por ti y de forma automática cuando utilices la interfaz de usuario de las soluciones alojadas. Para obtener más información, consulta el artículo de combinación de commits al fusionar una rama de Git con Bitbucket.
Resumen
El comando git rebase te permite modificar el historial y, con la reorganización interactiva puedes hacerlo todo sin dejar un rastro desordenado. De este modo, tienes libertad para cometer y corregir errores y pulir tu trabajo mientras que el historial del proyecto se mantiene limpio y lineal.
La red de seguridad: git reflog
Los registros de referencia ("reflogs") son un mecanismo que Git utiliza para registrar las actualizaciones aplicadas a los extremos de las ramas y otras referencias de commits. El comando reflog te permite ir a un commit aunque no se haga referencia a este en ninguna rama o etiqueta. Después de reescribir el historial, el registro de referencia contiene información sobre el antiguo estado de las ramas y te permite volver a ese estado, si es necesario. Cada vez que la punta de tu rama se actualiza por cualquier motivo (intercambio de ramas, adición de cambios, reescritura del historial o adición de nuevos commits), se añade una nueva entrada al registro. En esta sección echaremos un vistazo al comando git reflog
y exploraremos algunos de sus usos más frecuentes.
Uso
git reflog
Este comando muestra el registro de referencia del repositorio local.
git reflog --relative-date
Así se muestra el registro de referencia con información de fecha relativa (por ejemplo, hace dos semanas).
Ejemplo
Para comprender el comando git reflog
, vamos a ver un ejemplo.
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
El registro de referencias anterior muestra que se ha cambiado de la rama principal a la 2.2 y luego se ha vuelto a cambiar a la principal. Luego, se ha aplicado un restablecimiento completo (hard reset) a una confirmación anterior. La actividad más reciente, denominada HEAD@{0}
, aparece en la parte superior.
Si resulta que has vuelto para atrás sin querer, el registro de referencias contendrá la rama principal de confirmación apuntando a (0254ea7)
, antes de que perdieras accidentalmente dos confirmaciones.
git reset --hard 0254ea7
Con el comando git reset, ahora es posible cambiar la rama principal a la confirmación anterior. Esto proporciona una especie de red de seguridad en caso de que se haya modificado el historial por error.
Es importante tener en cuenta que el registro de referencia solo proporciona esta "red de seguridad" si los cambios se han confirmado en tu repositorio local y que solo realiza el seguimiento de los movimientos del extremo de la rama del repositorio. Además, las entradas del registro tienen una fecha de vencimiento. El periodo de tiempo por defecto antes del vencimiento de una entrada es de 90 días.
Si quieres más información, consulta la página sobre git reflog.
Resumen
En este artículo se han comentado distintos métodos para modificar el historial de Git y deshacer los cambios realizados. Asimismo, hemos visto el proceso git rebase en general. Algunas de las ideas fundamentales son las siguientes:
- Hay muchas maneras de reescribir el historial con Git.
- Usa
git commit --amend
para modificar el mensaje de registro más reciente. - Usa
git commit --amend
para hacer cambios al commit más reciente. - Usa
git rebase
para combinar commits y modificar el historial de una rama. - El comando
git rebase -i
proporciona un control mucho más exhaustivo sobre las modificaciones del historial que el git rebase estándar.
Aprende más sobre estos comandos en sus páginas individuales:
Compartir este artículo
Tema siguiente
Lecturas recomendadas
Consulta estos recursos para conocer los tipos de equipos de DevOps o para estar al tanto de las novedades sobre DevOps en Atlassian.