git — Editar commits
git es un sistema de control de versiones distribuido. Cada commit en git es inmutable: una vez creado, su contenido y su hash SHA-1 son fijos. "Editar" un commit en realidad significa crear uno nuevo que reemplaza al viejo, y mover la referencia del branch para que apunte ahí. El commit original queda huérfano hasta que el garbage collector lo limpia.
Esta sección cubre los flujos comunes para "editar" commits: cambiar el mensaje, cambiar el contenido, combinar commits, eliminarlos, reordenarlos, y recuperar trabajo cuando algo sale mal.
Idea base: "editar" = reescribir historia
Un commit en git contiene tres cosas que determinan su hash:
- El árbol de archivos (snapshot completo del repo en ese punto).
- El padre (commit anterior).
- Metadata: autor, fecha, mensaje.
Si cambias cualquiera de las tres, el hash cambia y el commit es técnicamente otro. Por eso decimos que git "rescribe historia" — siempre genera commits nuevos en lugar de mutar los existentes.
Esto tiene una consecuencia práctica que define todas las reglas:
Reescribir historia local es libre. Reescribir historia pública es peligroso.
Los comandos de esta página son seguros si el commit aún no fue pusheado al remoto. Si ya lo pusheaste y otros lo han pulled, hay que coordinar con el equipo antes de aplicarlos (o aceptar que se rompen los clones de los demás).
Modificar el último commit: --amend
--amend reemplaza el commit más reciente con uno nuevo. Es el caso más común: te das cuenta de un error apenas haces commit y quieres corregirlo.
Cambiar solo el mensaje
# Pasando el mensaje en la línea de comando
git commit --amend -m "nuevo mensaje aquí"
# Abriendo el editor con el mensaje actual para modificarlo
git commit --amend
Cambiar el contenido (agregar archivos olvidados)
# 1. Stagear lo que falta
git add archivo_olvidado.py
# 2. Amendar manteniendo el mismo mensaje
git commit --amend --no-edit
# 3. (O amendar y modificar también el mensaje)
git commit --amend -m "feat(add): incluye archivo_olvidado.py"
--no-edit es la flag que dice "reusa el mensaje del commit actual" — sin ella, git abriría el editor.
Quitar archivos del último commit
# 1. Sacar el archivo del commit (sigue en el working tree)
git reset HEAD~ -- archivo_a_quitar.py
# 2. Re-commitear sin él
git commit --amend --no-edit
Modificar commits anteriores al último: rebase -i
--amend solo toca el último commit. Para editar uno más atrás se usa rebase interactivo.
git rebase -i HEAD~3 # los últimos 3 commits
git rebase -i <hash> # rebase desde un commit específico (exclusive)
git abre un editor con una lista de commits y palabras clave que indican qué hacer con cada uno:
pick abc1234 feat(add): primer commit
pick def5678 feat(add): segundo commit
pick ghi9012 feat(add): tercer commit
# Comandos:
# p, pick = usar el commit
# r, reword = usar el commit, pero cambiar su mensaje
# e, edit = usar el commit, pero detener para modificar contenido
# s, squash = usar el commit, pero fusionar con el anterior (combina mensajes)
# f, fixup = como squash, pero descarta el mensaje de este commit
# d, drop = eliminar el commit
Para reordenar: cambias el orden de las líneas en el editor antes de guardar.
Cambiar solo el mensaje de un commit antiguo
reword abc1234 feat(add): primer commit
pick def5678 feat(add): segundo commit
pick ghi9012 feat(add): tercer commit
Al guardar, git va a parar en el commit marcado reword y abre el editor para que cambies el mensaje. Tras guardar, continúa con los demás.
Combinar varios commits en uno (squash)
pick abc1234 feat(add): empezar feature
squash def5678 feat(add): seguir feature
squash ghi9012 feat(add): terminar feature
git fusiona los tres en uno solo y abre el editor con los tres mensajes concatenados para que escribas el mensaje final.
Modificar el contenido de un commit antiguo
git va a parar en abc1234, te deja en un working tree donde puedes hacer cambios, stage, y luego:
# Después de hacer tus cambios:
git add archivos_modificados
git commit --amend # o --amend --no-edit
git rebase --continue # vuelve a aplicar los commits siguientes
⚠️ Si un commit posterior depende de algo que cambiaste en el commit editado, puede haber conflictos de rebase. git te detiene, los resuelves,
git add,git rebase --continue. Si te enredas:git rebase --abortdeja todo como estaba antes.
Cuando el commit ya está pusheado: force push
Si ya pusheaste y necesitas amendar:
--force vs --force-with-lease
| Flag | Comportamiento | Cuándo usar |
|---|---|---|
--force (-f) |
Pisa el remoto sin verificar nada. Si alguien empujó commits desde tu último fetch, se pierden | Casi nunca. Solo en branches personales sin colaboradores |
--force-with-lease |
Aborta el push si el remoto recibió commits nuevos desde tu último fetch | Default seguro para reescribir historia pusheada |
--force-with-lease es la versión "con cinturón de seguridad": revisa que tu copia local del estado remoto coincida con el estado actual del remoto antes de pisar. Si alguien más empujó algo, falla y tú decides qué hacer.
Reglas de cortesía
- Nunca force-pushear a
main/master/ branches compartidos sin aviso explícito al equipo. - Quien ya hizo
git pullantes del force-push tiene el commit viejo en su clon y necesita resincronizarse (git fetch && git reset --hard origin/<branch>). - En PRs activos, force-push después de un review puede invalidar comentarios atados a líneas específicas. Algunas plataformas (GitHub, GitLab) lo manejan, pero el flujo no es el mismo.
Red de seguridad: git reflog
Cuando reescribes historia, los commits "viejos" no desaparecen inmediatamente — quedan huérfanos por un tiempo (default ~90 días) y siguen accesibles vía hash.
1a2b3c4 HEAD@{0}: commit (amend): nuevo mensaje
5d6e7f8 HEAD@{1}: commit: mensaje viejo que querías recuperar
9a8b7c6 HEAD@{2}: checkout: moving from main to feature
reflog muestra todas las posiciones por las que pasó tu HEAD, incluso las que ya no son alcanzables desde ningún branch. Para volver a un commit que pensabas perdido:
git reset --hard 5d6e7f8 # ¡destructivo! revisa que sea el commit correcto
# o más seguro:
git checkout -b recuperado 5d6e7f8 # crea un branch nuevo apuntando ahí
El reflog es local: solo sabe de operaciones hechas en tu clon. No te ayuda a recuperar trabajo de otra máquina ni del remoto. Tampoco se transfiere con
cloneopush.
Cuándo NO usar amend / rebase
- El commit ya fue pusheado a un branch compartido y otros han hecho pull. Reescribirlo rompe sus clones.
- Estás en
main/mastercon protección de branch. La política del repo lo bloqueará (correctamente). - No estás seguro de qué cambia el reescritor. Probar en un branch desechable primero —
git branch tmpantes de un rebase es gratis y te deja un punto de regreso.
Tabla resumen
| Quiero... | Comando |
|---|---|
| Cambiar el mensaje del último commit | git commit --amend -m "nuevo" |
| Agregar un archivo olvidado al último commit | git add f && git commit --amend --no-edit |
| Cambiar el mensaje de un commit antiguo | git rebase -i HEAD~N → reword |
| Combinar varios commits en uno | git rebase -i HEAD~N → squash |
| Eliminar un commit del historial | git rebase -i HEAD~N → drop |
| Modificar el contenido de un commit antiguo | git rebase -i HEAD~N → edit |
| Reordenar commits | git rebase -i HEAD~N → reordenar líneas |
| Recuperar un commit "perdido" | git reflog + git reset --hard <hash> |
| Subir cambios después de reescribir | git push --force-with-lease |
| Cancelar un rebase a medias | git rebase --abort |