Capítulo 27 de 33 9 secciones 7 min

Reescribir la historia sin quemar al equipo

Amend, rebase y la única regla que no se rompe

<strong>Se puede reescribir todo lo que todavía no publicaste, y nada de lo que ya está en una rama compartida.</strong> Esa es la única regla. Para el último commit, <code>git commit --amend</code>; para varios, <code>git rebase -i</code>. Y hay que entender qué pasa debajo: reescribir no edita commits, crea commits nuevos con otro hash, y por eso rompe la copia de quien ya los tenía.

Este capítulo va con una advertencia por delante, y no es de las de adorno. Todo lo que hay aquí es seguro mientras la rama viva solo en tu máquina, y es un problema en cuanto la compartes 🚧

Lo que de verdad pasa cuando reescribes

Un commit es su contenido más su mensaje más su padre más quién y cuándo. Cambia cualquiera de esas cosas y el hash cambia, o sea que no estás editando un commit, estás creando otro.

Los originales siguen ahí (en el reflog de 25), pero tu rama ya apunta a los nuevos. Si alguien más tenía los viejos, para Git sus commits y los tuyos no tienen nada que ver 💥

Arreglar el último commit

El caso más común de todos: te comiste una palabra en el mensaje, o se te olvidó un archivo.

mkdir ventas-miss-yera
cd ventas-miss-yera
git init -q
printf 'ciudad,monto\nLima,1200\n' > ventas.csv
git add ventas.csv
git commit -q -m "Primeras vetnas de Lima"
git log --oneline
0d2977e Primeras vetnas de Lima
git commit -q --amend -m "Primeras ventas de Lima"
git log --oneline
54da6e2 Primeras ventas de Lima

Mensaje arreglado y el hash cambió. Míralo bien, porque ahí está todo el capítulo: no se corrigió el commit, se hizo otro 🔁

Agregar un archivo olvidado

printf 'canal,monto\nBodegas,4200\n' > canales.csv
git add canales.csv
git commit -q --amend --no-edit
git show --stat --oneline HEAD | head -5
479dd65 Primeras ventas de Lima
 canales.csv | 2 ++
 ventas.csv  | 2 ++
 2 files changed, 4 insertions(+)

El --no-edit mantiene el mensaje tal cual. Ahora el commit lleva los dos archivos, como si nunca hubiera pasado nada 🤫

Varios commits a la vez

Para esto está el rebase interactivo. Normalmente abre un editor; aquí lo hacemos sin editor, que es lo mismo y se puede ejecutar:

printf 'Arequipa,890\n' >> ventas.csv
git add ventas.csv
git commit -q -m "wip"
printf 'Cusco,760\n' >> ventas.csv
git add ventas.csv
git commit -q -m "wip 2"
printf 'Trujillo,1450\n' >> ventas.csv
git add ventas.csv
git commit -q -m "wip 3"
git log --oneline
a4dd965 wip 3
d643ffa wip 2
2aec8fa wip
479dd65 Primeras ventas de Lima

Tres commits que no le dicen nada a nadie. Los juntamos en uno con mensaje decente:

git reset -q --soft HEAD~3
git commit -q -m "Entran las ventas de Arequipa, Cusco y Trujillo"
git log --oneline
cat ventas.csv
dfb299d Entran las ventas de Arequipa, Cusco y Trujillo
479dd65 Primeras ventas de Lima
ciudad,monto
Lima,1200
Arequipa,890
Cusco,760
Trujillo,1450

Ese reset --soft es la forma más simple de juntar commits: mueve la etiqueta tres atrás pero deja todos los cambios señalados, así que un commit nuevo los recoge todos. Es lo que hace squash por dentro 🧵

Cambiar el orden o borrar uno

Para eso sí hace falta el rebase interactivo, que se lanza con git rebase -i HEAD~3 y abre una lista como esta:

pick 8a2b19c Entran las ventas de Arequipa
pick 3c1f80a Entran las ventas de Cusco
pick 5d9c2f1 Entran las ventas de Trujillo

# pick   = dejarlo como está
# reword = cambiar el mensaje
# squash = juntarlo con el de arriba
# drop   = borrarlo

Cambias la primera palabra de cada línea, guardas y cierras. Git rehace la historia siguiendo tus instrucciones. Y si algo sale mal a mitad, git rebase --abort lo deja todo como estaba 🧯

La regla

No reescribas historia que ya publicaste. No porque Git no te deje, sino porque cada persona que tenga esos commits va a tener que arreglar su copia a mano.

Si por lo que sea hay que hacerlo, se avisa antes y se usa --force-with-lease en vez de --force, que al menos se niega si alguien subió algo mientras tanto 🛑

La trampa

Tus commits tenían mensajes feos, los ordenas con un rebase interactivo y quedan preciosos. La rama ya estaba subida.

$ git rebase -i HEAD~3
$ git push --force origin reporte-por-canal

# en el chat, media hora despues:
# "me sale que mi rama diverge, que hago"
# "a mi me aparecen los commits duplicados"
Qué está mal

Reescribir cambia los hashes, y con eso rompiste la rama de todos 💥

Un rebase no edita commits: crea commits nuevos con el mismo contenido y otro hash. Para Git, la historia de tu compañera y la tuya ya no tienen nada que ver, aunque el texto sea idéntico.

De ahí la regla que se repite en todos lados y casi nunca se explica: no reescribas historia que ya publicaste. Mientras la rama solo esté en tu máquina, haz lo que quieras.

Si ya pasó, se avisa antes de nada y se acuerda quién hace qué. Un --force silencioso en una rama compartida es lo que peor sienta en un equipo.

Comprueba que se entendió

Comprueba que lo tienes

Hiciste --amend sobre un commit que ya habías subido. ¿Qué pasó?

  • Nada, es el mismo commit corregido
  • Ahora tu rama y la de allá tienen historias distintas
  • GitHub lo actualiza solo al hacer push
  • Se borró el commit anterior

Ejercicios

1. Arregla un mensaje mal escrito

Crea el catálogo con un error de dedo y corrígelo.

cd ..
mkdir catalogo
cd catalogo
git init -q
printf 'bodega,ciudad\nBodega Inti,Cusco\n' > bodegas.csv
git add bodegas.csv
git commit -q -m "Catlaogo inicial de bodegas"
git log --oneline
git commit -q --amend -m "Catalogo inicial de bodegas"
git log --oneline
3f1bfd5 Catlaogo inicial de bodegas
a9874ae Catalogo inicial de bodegas

Mensaje arreglado y hash distinto. Los dos hashes están a la vista.

2. Agrega un archivo olvidado

Métele el README al commit que ya hiciste.

printf '# Catalogo de bodegas\n' > README.md
git add README.md
git commit -q --amend --no-edit
git show --stat --oneline HEAD | head -5
9d02d3f Catalogo inicial de bodegas
 README.md   | 1 +
 bodegas.csv | 2 ++
 2 files changed, 3 insertions(+)

Dos archivos en un solo commit, sin uno de "se me olvidó el README".

3. Haz tres commits desordenados

Los típicos "wip" de una tarde.

printf 'Minimarket Sol,Trujillo\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "wip"
printf 'Bodega Sur,Arequipa\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "wip2"
printf 'Mayorista Norte,Piura\n' >> bodegas.csv
git add bodegas.csv
git commit -q -m "wip3"
git log --oneline
f4f8d23 wip3
e831802 wip2
543805b wip
9d02d3f Catalogo inicial de bodegas

Tres commits que dentro de un mes no le dicen nada a nadie.

4. Júntalos en uno

Con reset --soft, que es lo que hace squash por dentro.

git reset -q --soft HEAD~3
git status --short
git commit -q -m "Entran las bodegas de Trujillo, Arequipa y Piura"
git log --oneline
M  bodegas.csv
85aada0 Entran las bodegas de Trujillo, Arequipa y Piura
9d02d3f Catalogo inicial de bodegas

Un commit con un mensaje que sí se entiende 🧵

5. Comprueba que no se perdió nada

El contenido tiene que ser exactamente el mismo.

cat bodegas.csv
git show --stat --oneline HEAD | head -4
bodega,ciudad
Bodega Inti,Cusco
Minimarket Sol,Trujillo
Bodega Sur,Arequipa
Mayorista Norte,Piura
85aada0 Entran las bodegas de Trujillo, Arequipa y Piura
 bodegas.csv | 3 +++
 1 file changed, 3 insertions(+)

Las cuatro bodegas están. Se juntó la historia, no los datos.

6. Recupera los commits originales

Reescribir no borra: el reflog sigue teniendo los viejos.

git reflog --format="%h %gs" | head -6
85aada0 commit: Entran las bodegas de Trujillo, Arequipa y Piura
9d02d3f reset: moving to HEAD~3
f4f8d23 commit: wip3
e831802 commit: wip2
543805b commit: wip
9d02d3f commit (amend): Catalogo inicial de bodegas

Ahí están los tres "wip" con sus hashes de antes 🧯

7. Intenta enmendar sin tener commits

Prueba --amend en un repositorio recién creado.

cd ..
mkdir vacio
cd vacio
git init -q
git commit --amend -m "algo"
fatal: You have nothing to amend.

No hay commit anterior que reescribir, y Git lo dice claro 🛑

Lo que te llevas

Reescribir crea commits nuevos con otro hash. Todo vale mientras la rama sea solo tuya.

Practica este capítulo 📓

Todo el código de arriba en un cuaderno que corre de principio a fin, y los ejercicios con una celda vacía para que los hagas tú. Se abre en Google Colab y no hay que instalar nada.

¿Tienes alguna duda o consulta?