Guía9 min

git reset para coding agents: mueve HEAD; --hard no es el undo

Resumen

git reset mueve la punta de la rama. --soft deja index y disco; --mixed (default) destagea; --hard pisa working tree y puede borrar untracked. Un agente no corre --hard ni reset de historia publicada. Pathspec destagea; para un archivo usa restore.

GitHub
Tres capas HEAD, index y working tree; reset --hard pisa las tres, --soft solo mueve HEAD

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

git reset no es undo de un archivo. El man page de Git (2.50, Apple Git-155) lo parte en dos: con <commit> mueve HEAD (y, según el modo, index y disco); con <pathspec> solo destagea. git(1) lo contrapone a restore (paths, no mueve la rama) y a revert (commit nuevo que deshace otro). Un coding agent que “limpia” con reset --hard tira el working tree entero, no el path que rompió.

Esta guía no sustituye worktrees ni gitignore. El contrato: cuándo un agente puede resetear HEAD, qué modos están prohibidos, y por qué pathspec no es --hard.

Tres capas (oficial)

Antes de reset [<mode>] <commit>, Git copia el tip actual a ORIG_HEAD. Luego:

ModoHEADIndexWorking tree
--softMueveIgualIgual
--mixed (default)MueveIguala al nuevo HEAD; nada stagedIgual
--hardMueveIgualaPisa tracked; puede borrar untracked
--keepMueveIguala paths distintos entre commit y HEADIguala esos paths; aborta si hay edits locales ahí
--mergeMueveResetea unmergedConserva edits no staged; aborta si un path distinto commit↔index tiene unstaged

--hard: “Overwrite all files and directories with the version from <commit>, and may overwrite untracked files”. Tracked que no están en <commit> se borran. No es “descartar un hunk”.

HEAD se mueve; --soft deja index y disco, --hard pisa las tres capas

Pathspec: destagear, no rebobinar

git reset [--] <pathspec> no mueve HEAD. Pone el index de esos paths al tree-ish (default HEAD). Es el inverso de git add <pathspec> y equivale a git restore --staged <pathspec>. El disco no cambia.

git reset -p es interactivo (inverso de git add -p). Un harness headless no lo corre.

Un agente que quiere “sacar este archivo del commit” no hace reset --hard HEAD~1. Hace git reset -- src/foo.ts (o restore --staged) y un commit nuevo. Historia publicada: PR con gh, no reset.

Lo que el agente sí / no corre

QuieroComandoTrampa
Destagear paths, dejar discogit reset -- src/foo.tsSin --, un path que parezca opción se come
Rehacer el último commit local no pusheadogit reset --soft HEAD^ + git commitHEAD^ en un merge es el primer padre; nombra el SHA
Destagear todo, dejar editsgit reset (= --mixed HEAD)No toca untracked; no es clean
Abortar un merge sucio conservando edits localesgit reset --merge ORIG_HEAD--hard ORIG_HEAD tira esos edits
Aislar commits prematurosgit branch topic/wip antes de mover mainEl man: crea rama, después --hard HEAD~n en la rama que vas a rebobinar

Prohibido en autónomo:

  • git reset --hard (con o sin commit). El man avisa overwrite de untracked. El humano lo pide por escrito o no se corre.
  • git reset --hard HEAD~n en una rama ya pusheada / compartida. El man: “Do not do this if you have already given these commits to somebody else.”
  • git reset sin pathspec en el checkout del humano. Mueve su HEAD.
  • --patch. Interactivo.
  • Confundir reset --soft HEAD^ con amend+force-push. Soft deja el árbol; el force es otro comando y otro permiso.

Receta para un agente (60 segundos)

El default correcto no es reset. Si el agente está en un worktree propio y solo necesita destagear:

git status
git reset -- src/lib/foo.ts src/lib/bar.ts
git status
git diff --cached

Si el último commit es suyo, no está en origin, y el ticket pide rehacer el mensaje/árbol:

git log -1 --oneline
git status -sb          # no debe decir ahead of origin con others
git reset --soft HEAD^
# edits / git add paths explícitos
git commit -c ORIG_HEAD

--soft HEAD^ + commit -c ORIG_HEAD es el ejemplo oficial de “undo a commit and redo”. No uses --hard. No uses HEAD~5 “por si acaso”.

ORIG_HEAD es el tip antes del reset. Úsalo para volver (git reset --keep ORIG_HEAD) si el soft/mixed salió mal. --keep aborta si hay edits locales en paths que cambiarían.

Pathspec destagea un archivo; --hard no es el atajo

Reset vs restore vs revert

git(1):

  • revert: commit nuevo que deshace otros. Historia hacia adelante. Eso va a un PR.
  • restore: copia paths desde index o un commit. No mueve la rama.
  • reset: mueve la punta de la rama (o, con pathspec, solo el index).

Un agente que “deshace el PR mergeado” no resetea main. Abre un revert en rama y abre PR.

Untracked: reset --mixed no los toca. reset --hard puede pisarlos. Quitar untracked es otro comando (git clean) y otro permiso.

Checklist

  • El agente no corre --hard salvo orden explícita del humano en su worktree.
  • Pathspec lleva --. Destagea; no rebobina.
  • Soft/mixed solo en commits no publicados. status -sb antes.
  • Historia compartida: revert + PR, no reset.
  • ORIG_HEAD existe después del reset; no asumas que HEAD^ sigue siendo el mismo.
  • --keep / --merge si hay edits locales que no quieres tirar. No improvisar --hard.
  • Cero reset en el checkout del humano.

FAQ

¿git reset sin args es seguro? Es --mixed HEAD: destagea todo, deja disco. No borra untracked. Sigue siendo un cambio de index en el repo del humano si no estás en worktree propio.

¿--soft HEAD^ pierde el commit? El commit queda reachable desde ORIG_HEAD y reflog. Deja de ser tip de la rama. Si ya lo pusheaste, el remoto no se entera hasta un force.

¿Puedo resetear un solo archivo a un commit viejo y el disco? No con reset <commit> -- file (solo index). Para disco: git restore --source=<commit> -- file. Reset pathspec no actualiza working tree.

¿--hard borra untracked? El man: may overwrite untracked files. No lo trates como “solo tracked”.

¿Submódulos? --recurse-submodules resetea el working tree de submódulos activos al commit del superproyecto (HEAD detached). Default: no. Un agente no lo añade.

El curso instalar un agente cubre el loop local. Aislamiento de red y secretos: sandboxing. Reset mueve punteros, no es sandbox.

Verificado 2026-09-03 contra git-reset(1) y git(1) (Git 2.50.1 / Apple Git-155).