Recetas Git: Modificar un commit antiguo

Cuando estamos trabajando en un proyecto versionado con Git a veces nos encontramos con que necesitamos modificar un commit que hicimos hace algún tiempo, pero que aún no hemos enviado al servidor (esto es importante, ya que no debemos modificar commits a los que ya hemos hecho push). ¿Cómo podemos hacerlo? Con un rebase interactivo.

Para empezar debemos averiguar el hash del commit anterior al que queremos modificar, para ello no hay más que hacer un git log y copiar el hash que nos interese:

> git log
[9ff028c] (Thu May 5 12:02:51 2011) Adds breadcrumbs styles -  Juan G. Hurtado
[61b5536] (Thu May 5 11:55:34 2011) Adds styles for horizontal separators on boxes - Juan G. Hurtado
[ac44e68] (Thu May 5 09:47:08 2011) Adds server messages styles - Juan G. Hurtado
[ff9c592] (Thu May 5 09:37:26 2011) Adds boxes to sample page - Juan G. Hurtado

Por ejemplo, si queremos modificar el commit “Adds styles for horizontal separators on boxes”, cuyo hash es “61b5536″, debemos quedarnos con el hash del commit anterior, que es el “ac44e68″. Una vez que lo tenemos ya podemos comenzar el rebase interactivo:

> git rebase -i ac44e68

Este comando nos abrirá el editor para Git que tengamos definido en el sistema con un texto parecido a este:

pick 61b5536 Adds styles for horizontal separators on boxes
pick 9ff028c Adds breadcrumbs styles

# Rebase ac44e68..9ff028c onto ac44e68
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

La primera parte del texto es un listado de los commits que van a formar parte del rebase, y lo que quieres hacer con ellos. Por defecto todos los commits tienen pick, que significa que no se hará nada con ellos, se quedarán igual. La segunda parte del texto es una ayuda que indica lo que puedes hacer con los commits: pick, reword, edit, squash, fixup y exec. En nuestro caso necesitamos editar un commit, así que usaremos edit. Editamos el texto, modificando la linea de nuestro commit para que quede tal que:

edit 61b5536 Adds styles for horizontal separators on boxes

Cuando salvemos y cerremos el editor con el texto modificado comenzará el rebase con las opciones que le hayamos indicado, en este caso editar un commit. Cuando editamos un commit lo que hace Git es movernos en el tiempo y llevarnos al proyecto en el estado que estaba justo después de hacer el commit que queremos editar. Estando en este momento temporal podemos hacer los cambios que necesitemos y añadirlos a nuestro commit como se hace normalmente:

> git add .
> git commit --amend

Cuando ya tenemos modificado el commit a nuestro gusto, continuamos con el rebase:

> git rebase --continue

Y ya ha quedado modificado el commit sin haber alterado su orden, sin haber hecho un commit nuevo, etc. No obstante es importante recordar que esto sólo podemos hacer con commits que aún no se hayan enviado al servidor (a los que no se haya hecho push).