whybother

Field Guide · 09 of 10

Why AI agents run git reset --hard and delete .git

When a session goes wrong, an agent may reach for the strongest undo it knows: move the branch back some number of commits, then delete the repository metadata to start fresh. The first command is often recoverable. The second removes the thing that made it recoverable. The defence is a checkpoint before the session, a rule against both commands, and a check that the history is still there afterwards.

What is actually happening

“Revert to a clean state” has many implementations in git. git reset --hard is the bluntest: it moves the branch to another commit and makes the working tree match it. The HEAD~40 in the skit is a number chosen by guess; on a shorter history git refuses with a fatal error.

In a scratch repository, reset --hard HEAD~40 moved the branch back 40 commits. Staged and unstaged edits to tracked files were overwritten, and a newly added file was removed. An untracked file was left alone. The 40 commits were not deleted: git reflog listed the old position and git reset --hard HEAD@{1} brought it back. The unstaged edit was gone for good. The staged content survived only as dangling objects that git fsck --lost-found listed, until git cleans them up. Reflog entries expire in time too.

Then comes rm -rf .git. History, branches, stashes, the reflog, remotes and hooks all live there. The working files stay, so the project looks intact until the next git command answers fatal: not a git repository. Commits that were pushed are still on the remote. Anything that was not pushed is gone.

Together they are worse than either: the reset is recoverable through the reflog, and the reflog lives in .git. “Start fresh” here means “forget”.

How it shows up in the diff

Mostly it does not, because the evidence is what gets removed. Look in the log and the state of the repository.

$ git reset --hard HEAD~40
HEAD is now at 3f9c2a1 Add cart total
$ rm -rf .git
$ git status
fatal: not a git repository (or any parent up to mount point /)

Guardrail

Take a checkpoint, forbid the two commands, verify afterwards.

Paste into AGENTS.md

Never run git reset --hard, git clean, git push --force or rm -rf on the repository or on .git. If the working tree is in a bad state, stop and describe it.

Check it mechanically

# before.sh, run before the session: refuse a dirty tree, then mark the starting point
if [ -n "$(git status --porcelain)" ]; then echo 'Commit or stash first:'; git status --short; exit 1; fi
git branch -f checkpoint/before-agent

# after.sh, run after the session in the same clone
if ! git merge-base --is-ancestor checkpoint/before-agent HEAD; then echo 'HEAD no longer contains the checkpoint'; exit 1; fi

Save each block as a script, since they use exit. The first stops on a dirty tree and lists the files, because uncommitted edits to tracked files do not come back after a reset. On a clean tree it prints nothing and creates the branch; run again, it moves the mark to the current commit, and it errors if you are standing on that branch. The checkpoint is a local branch, so the second script belongs in the same clone, not on a fresh CI checkout. It passes while HEAD descends from the checkpoint, including with uncommitted edits on top. It fails after a reset past the checkpoint or onto different commits, and after rm -rf .git, where git prints its own fatal error first. A reset that stays above the checkpoint passes.

Rules are requests. A sturdier barrier is to give the agent less to destroy: a linked worktree on its own branch.

git worktree add -b agent ../agent main

In a linked worktree .git is a one-line file pointing back at the main repository. Removing it deletes that pointer, not the history, and a reset moves only that worktree’s branch.

That is all it protects against: reset --hard and rm -rf .git on that checkout. Branches, reflogs and objects are shared with the main repository, so git branch -D main, a reflog expiry followed by git gc, or a force push run from the worktree can still reach it. A throwaway clone keeps the first two out of reach, but its origin points at the original, so a force push can still land there.

Further reading

Pro Git: free to read online. The chapters on reset and on data recovery cover both halves of this entry; read them before the day you need them.