Shadow Git checkpoints for any directory
Safe rollback should not depend on whether the directory you are editing already has a `.git/`. A small shadow-Git tool turned out to be enough: isolate `GIT_DIR`, `GIT_WORK_TREE`, and `GIT_INDEX_FILE`, then disable hooks so bookkeeping commits stay bookkeeping.
I wanted checkpoint and restore semantics outside a normal Git repo, without polluting the target directory or tripping the user's hooks. The answer was to treat Git like plumbing: separate the object store, the work tree, and the index.
Most rollback tooling quietly assumes the thing you are editing is already a normal Git repository.
That is a bad assumption.
Agents touch plenty of directories that are not clean social repos:
- temporary worktrees
- generated sites
- unpacked archives
- scratch directories
- project folders where you want safety, not history
I wanted the same checkpoint/diff/restore loop there too, without creating or
touching a live .git/ inside the target directory.
So I built a standalone tool:
scripts/git-shadow-checkpoint.py
It snapshots any directory into an isolated shadow Git repo.
The problem with the obvious approach
If you run git init inside the target directory, you immediately collapse two
different jobs into one state store:
- social history: commits you want to keep and share
- safety snapshots: throwaway recovery points you want before risky edits
That is already messy inside a normal repository.
It is worse in a non-repo directory, because now your rollback primitive has changed the directory you were supposedly protecting.
The right boundary is simpler:
- keep the target directory as just files
- keep the checkpoint state somewhere else
- let Git index the target directory without becoming its visible repo
The three variables that make this work
The key move was treating Git like plumbing instead of porcelain.
The tool isolates three things:
GIT_DIR— where the shadow repository metadata livesGIT_WORK_TREE— which directory should be snapshottedGIT_INDEX_FILE— where the staging index should live
That third one matters more than most people realize.
Pointing only GIT_DIR and GIT_WORK_TREE at a foreign directory is not
enough. Git still wants to use the repo’s normal index conventions unless you
tell it otherwise. For non-Git directories, or directories adjacent to another
repo, that leads to exactly the kind of accidental coupling you were trying to
avoid.
So the tool puts the index inside the shadow repo too.
That gives one isolated state bundle:
- objects
- refs
- index
The working directory stays just a working directory.
The second trick: disable hooks completely
There was another dumb failure mode hiding here.
Even if the shadow repository is isolated, Git hooks do not know that your commit is internal bookkeeping. A global or inherited hook can still fire and block the snapshot.
That is wrong for this use case.
These commits are not publication events. They are recovery artifacts.
So the tool forces:
core.hooksPath=/dev/null--no-verify
for the shadow commits.
That makes the checkpoint path fail less often and, more importantly, fail for the right reasons.
If a checkpoint fails, it should be because the shadow repo is broken, not
because some unrelated prevent-master-commits hook decided to moralize about
an internal save point.
What shipped
The tool is deliberately small. It supports:
init— create the isolated shadow repocheckpoint— snapshot the current directory statelist— inspect recent checkpointsdiff— compare working tree vs checkpointrestore— restore one or more files from a checkpoint
It also has focused tests for the boundary that matters:
- works in a real Git repo without polluting the real repo
- works in a directory with no
.git/at all - captures untracked changes
- bypasses normal hook surfaces
- restores files from earlier checkpoints
That last point is what made this worth shipping.
The moment you can checkpoint arbitrary directories, rollback stops being a special feature reserved for “properly set up” repos. It becomes a cheap safety primitive you can use almost anywhere.
Why this is a better primitive than hidden checkpoints
I already wrote about why I do not want invisible checkpoint spam mixed into main Git history.
This tool keeps the same stance.
It is not a replacement for real commits. It is not a substitute for review. It is not “agent memory.”
It is a safety rail:
- cheap to create
- cheap to diff
- cheap to throw away
That is the right level.
Agents need recovery surfaces all the time. They do not need more secret state pretending to be history.
The broader lesson
Git is much more composable than most agent tooling gives it credit for.
People often treat Git as one thing:
the repo in the current directory.
But Git is really a set of separable stores and pointers:
- object database
- refs
- index
- work tree
Once you separate those pieces, a lot of useful tooling gets much easier:
- shadow checkpoints
- per-session diff stores
- isolated rollback systems
- durable artifact ledgers without history pollution
The trick is to keep the boundary honest.
Use Git for storage and diffing where it helps. Do not pretend every storage use should become user-visible history.