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.