Reescritura del historial

Reescritura del historial

Introducción

En este tutorial se explicarán varios métodos para reescribir y modificar el historial de Git. Git usa distintos métodos para registrar los cambios. Hablaremos de las ventajas e inconvenientes de cada uno y daremos 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 posibles peligros derivados.

El trabajo principal de Git es garantizar que nunca pierdas un cambio confirmado. No obstante, también está diseñado para otorgarte un control total sobre tu flujo de trabajo de desarrollo. Por tanto, te permite definir exactamente el aspecto del historial de tu proyecto; sin embargo, también introduce la posibilidad de perder confirmaciones. 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 flujo de trabajo. Al terminar este tutorial, te habrás familiarizado con los comandos que te permiten reestructurar las confirmaciones de Git y sabrás cómo evitar los peligros habituales de reescribir el historial.

Modificación de la última confirmación: git commit --amend

El comando git commit --amend es una manera práctica de modificar la confirmación más reciente. Te permite combinar los cambios preparados con la confirmación anterior en lugar de crear una confirmación nueva. También puede usarse para editar el mensaje de la confirmación anterior sin cambiar la instantánea. Sin embargo, el comando no se limita a alterar la confirmación más reciente, sino que la sustituye por completo, por lo que la confirmación corregida será una entidad nueva con su propia referencia. Para Git, se verá como una confirmación nueva, que 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 trataremos en los apartados siguientes.

git commit amend

Modificación del mensaje del commit de Git más reciente

git commit --amend

Digamos que acabas de realizar una confirmación y te das cuenta de que hay un error en el mensaje de registro. Ejecutar este comando cuando no se ha preparado nada te permite editar el mensaje de la confirmación sin modificar su instantánea.

Se realizan confirmaciones prematuras con mucha frecuencia durante el curso del desarrollo. Es fácil olvidarse de preparar un archivo o dar el formato incorrecto al mensaje de la confirmación. El indicador --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 confirmarlo con el indicador --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

El indicador --no-edit te permite hacer las correcciones en la confirmación sin cambiar el mensaje. La confirmación resultante reemplazará a la incompleta y parecerá que hemos confirmado los cambios en hello.py y main.py en una sola instantánea.

No modifiques commits públicos

Las confirmaciones corregidas son, de hecho, confirmaciones totalmente nuevas, y la confirmación anterior ya no estará disponible en tu rama actual. Esto tiene las mismas consecuencias que restablecer una instantánea pública. Procura no corregir una confirmación en la que otros desarrolladores hayan basado su trabajo, ya que crea una situación confusa para ellos de la que resulta complicado recuperarse.

Resumen

Repasemos: git commit --amend te permite añadir los nuevos cambios preparados a la confirmación más reciente. Puedes añadir o eliminar cambios del entorno de ensayo de Git con una confirmación --amend. Si no hay ningún cambio preparado, --amend seguirá pidiéndote que modifiques el mensaje de registro de la última confirmación. Ten cuidado al usar --amend en confirmaciones compartidas con otros miembros del equipo. Puede que modificar una confirmación compartida con otro usuario conlleve aplicar resoluciones del conflicto de fusión largas y confusas.

Modificación de confirmaciones más antiguas o varias confirmaciones al mismo tiempo

Si quieres modificar confirmaciones más antiguas o varias confirmaciones a la vez, puedes usar git rebase para combinar una secuencia de confirmaciones en una nueva confirmación base. En el modo estándar, git rebase te permite reescribir el historial, aplicando de forma automática las confirmaciones de tu rama de trabajo actual al encabezado de la rama anterior. Dado que las nuevas confirmaciones reemplazarán a las antiguas, es importante que no uses git rebase en confirmaciones que se han hecho públicas, o parecerá que el historial de tu proyecto ha desaparecido.

En estas instancias u otras similares en las que sea importante mantener limpio el historial del proyecto, añadir la opción -i al comando git rebase te permitirá ejecutar rebase interactive. Esta opción te da la oportunidad de alterar confirmaciones individuales durante el proceso, en lugar de mover todas las confirmaciones. 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 esa confirmación 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 confirmación de Git contará con un mensaje de registro que explique lo ocurrido en la confirmación. Estos mensajes proporcionan información valiosa sobre el historial del proyecto. Durante la reorganización, puedes ejecutar distintos comandos en las confirmaciones para modificar esos mensajes.

  • El comando reword o "r" (reformulación) detendrá la reproducción de la reorganización y te permitirá reescribir el mensaje de una confirmación concreta durante el proceso.
  • Cuando se ejecuta el comando squash o s (combinación) durante la reproducción de la reorganización, se detiene el proceso y se te solicitará que edites los mensajes de confirmación individuales para formar un mensaje combinado. En la siguiente sección encontrarás más información sobre combinación de confirmaciones.
  • El comando fixup o "f" (corrección) tiene el mismo efecto de combinación que el comando squash. Al contrario que este, las correcciones de confirmaciones no interrumpen la reproducción de la reorganización para abrir un editor y combinar mensajes de confirmación. Los mensajes de las confirmaciones marcadas como "f" se descartarán para mantener el mensaje de confirmación anterior.

Combinación de commits para un historial limpio

El comando s (squash) nos permite ver la verdadera utilidad de la reorganización. Este comando de combinación te permite especificar qué confirmaciones quieres fusionar con las confirmaciones anteriores. Así, el historial se mantendrá limpio. Durante la reorganización, Git ejecutará los comandos rebase especificados para cada confirmación. En el caso de las confirmaciones tipo squash, Git abrirá el editor de texto configurado y te pedirá que combines los mensajes de las confirmaciones especificadas. Este proceso se puede representar de la siguiente forma:

Tutorial de Git: ejemplo de git rebase -i

Ten en cuenta que las confirmaciones modificadas con el comando rebase tienen un ID distinto del de las confirmaciones originales. Las confirmaciones marcadas con pick tendrán un nuevo ID si las confirmaciones anteriores se han reescrito.

Las soluciones de almacenamiento de Git modernas, como Bitbucket, ahora ofrecen funciones automáticas de fusión mediante combinación con "squash". Estas funciones llevan a cabo una reorganización y combinan las confirmaciones de una rama de forma automática cuando utilices la interfaz de usuario de las soluciones alojadas. Para obtener más información, consulta el artículo sobre combinación de confirmaciones 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 confirmaciones. El comando reflog te permite ir a una confirmación aunque no se haga referencia a esta 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 el extremo de tu rama se actualiza por cualquier motivo (intercambio de ramas, incorporación de nuevos cambios, reescritura del historial o adición de nuevas confirmaciones), 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 la información de fecha correspondiente (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 master
c10f740 HEAD@{2}: checkout: moving from master to 2.2

El registro de referencia anterior muestra que se ha cambiado de la rama maestra a la 2.2 y luego se ha vuelto. A partir de ahí, se ha aplicado un restablecimiento completo a una confirmación anterior. La actividad más reciente se representa en la parte superior denominada HEAD@{0}.

Si resulta que has retrocedido sin querer, el registro de referencia conservará la confirmación master apuntando a (0254ea7), como antes de que perdieras accidentalmente dos confirmaciones.

git reset --hard 0254ea7

Con el comando git reset, ahora es posible cambiar la maestra a la confirmación anterior. Esto proporciona una 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 predeterminado antes del vencimiento de una entrada es de 90 días.

Para obtener más información, consulta la página sobre git reflog

Resumen

En este artículo hemos comentado varias formas de modificar el historial de Git y deshacer los cambios de Git. Le hemos echado un amplio vistazo al proceso de reorganización de Git. Algunas de las ideas fundamentales son las siguientes:

  • Hay muchas formas de reescribir un historial con Git.
  • Se puede usar git commit --amend para modificar el último mensaje de registro.
  • Se puede usar git commit --amend para aplicar modificaciones a la confirmación más reciente.
  • Se puede usar git rebase para combinar confirmaciones y modificar el historial de una rama.
  • git rebase -i proporciona un control más amplio de las modificaciones del historial que un comando git rebase estándar.

Obtén más información sobre los comandos que hemos mencionado en sus páginas individuales: