Skip to main content

🛟 gitundo

The undo button git never had.

Automatic safety‑net snapshots for any git repository — so you can never lose work to reset --hard, clean -fdx, a bad rebase, or "oops I deleted it" again.

Zero dependencies · works in any repo · never touches your history.

pip install gitundo

PyPI version Python versions License: MIT CI VS Code extension

Features · Quick start · Commands · Auto-guard · How it works · Recipes · Comparison · FAQ


The one-paragraph pitch

Git only protects work you commit. Everything else — the half‑finished refactor, the deleted file, the unstaged fix that took an hour — is one reset --hard away from oblivion. gitundo is a safety net for exactly that:

  • gitundo snap stores the entire current state of your working tree (staged and unstaged and untracked) as a lightweight checkpoint.
  • gitundo restore brings any checkpoint back — even files you deleted.
  • The auto-guard (gitundo autowrap) snapshots automatically before destructive commands like git reset --hard or git clean -fdx.

Checkpoints live on a hidden ref — refs/gitundo/checkpoints — so they are ordinary git objects: deduplicated by git, visible in git log, shareable, and safe. Your branches, commits, index and tags are never touched.


Features

📸 Snapshot anything Staged, unstaged, and untracked files, deletions, symlinks, binaries. Ignored files and the stash are never captured.
🪂 Undo the un‑undoable Recover files destroyed by reset --hard, clean -fdx, checkout -f, bad merges, editor mishaps — even files that were never committed.
🛡️ Auto‑guard Optional shell wrapper that snapshots before clearly destructive git commands. You are always one command from a safety net.
🏷️ Tag & diff Name checkpoints (gitundo tag demo-ready), inspect exactly what each one saved (gitundo diff).
🔒 Zero‑invasion Only a hidden ref is ever written. The index, working tree, and history stay exactly as they were.
🪶 Zero dependencies Pure standard library. Works everywhere git works.
🚿 Prune gitundo prune -k 50 keeps your latest 50 (+ tagged) and reclaims the rest.
🧵 Library + CLI Use it from your own scripts via from gitundo import core.
🧑‍💻 VS Code extension Official editor integration — sidebar checkpoints, one-click snapshot & restore, diff, tags and a live status bar (see extensions/vscode).

Quick start

# 1. install
pip install gitundo          # or: uv tool install gitundo / pipx install gitundo

# 2. snapshot whenever you reach a point you might want to return to
gitundo snap "before the big refactor"

# ...edit files, experiment, break things...

# 3. bring back the state from any snapshot
gitundo list                 # see what you've saved
gitundo restore              # latest snapshot
gitundo restore 2            # two snapshots ago
gitundo restore before-the-big-refactor   # by tag

The one-command setup you'll actually use

gitundo on        # mark this repo as guarded
gitundo autowrap  # install the shell wrapper (one time, per machine)

Editor integration (VS Code)

The official gitundo extension puts a 🛟 GitUndo view in your Activity Bar: snapshot with one click, browse checkpoints, diff what each one saved, and restore from the right-click menu — all without touching your commits.

git clone https://github.com/xrchris7/gitundo
cd gitundo/extensions/vscode
npm install && npm run package        # -> gitundo-<version>.vsix
code --install-extension gitundo-0.1.0.vsix

(Needs the CLI installed: pip install gitundo.) Full details in extensions/vscode/README.md.

Now every time you run a destructive command, gitundo quietly saves a checkpoint first:

$ git reset --hard HEAD~3
gitundo  snapshot before git reset --hard → 9f3a1c2
HEAD is now at 7b8c9d0 add feature

$ git clean -fdx
gitundo  snapshot before git clean -dfx → 0e5d2b1
Removing build/cache/

# hours later — "wait, that reset was a mistake" →
$ gitundo restore
✔ restored working tree to latest (14 file(s) written, 0 removed)

Disable the guard at any moment with export GITUNDO_DISABLE=1.


Commands

Command What it does
gitundo snap [msg] [-t TAG] Snapshot the current working state.
gitundo list [-n N] [--tags] List checkpoints, newest first.
gitundo restore [SEL] [--index] [--hard] [--delete-extraneous] Restore the working tree to a checkpoint.
gitundo diff [SEL] [--from SEL] [--workdir] Show what a snapshot saved, or diff two snapshots.
gitundo tag NAME [SEL] / gitundo untag [NAME] Give checkpoints readable names.
gitundo prune -k N Delete old checkpoints (tagged ones survive).
gitundo status Guard state + recent checkpoints + storage used.
gitundo on / gitundo off Enable/disable the guard for this repo.
gitundo autowrap [--uninstall] Install/remove the destructive-command shell wrapper.
gitundo help Full command reference.

Selectors — everywhere a checkpoint is expected:

latest | N (0 = latest, 1 = the one before…) | a tag name | an object-id prefix

Full reference in docs/CLI.md.


The auto-guard

gitundo autowrap adds a tiny function to your ~/.bashrc / ~/.zshrc that intercepts git and, only when the command is clearly destructive, snapshots first, then runs your command normally (stdin/stdout/exit code all inherited). Nothing else changes — git status is never slowed down.

Currently guarded: git reset --hard, git clean -dfx, git checkout -f / -- / ., git restore, git rm, git branch -D, git update-ref, plus plain-shell rm -rf, rmdir, shred, mv/cp, truncate, dd and in-place sed -i-style edits when run through the wrapped git.

History operations like merge, rebase, revert and cherry-pick are deliberately not guarded: git's own reflog already protects them and they never delete uncommitted work — auto-snapshots would only add noise.


How it works

Snapshots are plain commits hanging off a hidden ref:

git commit                                  ← the snapshot
tree  f02a9…  ← complete working state: tracked + untracked files as-is
author    gitundo <gitundo@localhost>       ← never impersonates you
message   before-the-big-refactor           (subject)
          snapshot-of: a1b2c3d              ← your HEAD at capture time
          created-by: gitundo
refs/gitundo/checkpoints  o3 → o2 → o1 → …   (linear chain of snapshots)
refs/gitundo/tags/demo-ready → o2            (tags are lightweight refs)

Because a checkpoint is a real commit, git does the heavy lifting for you:

  • Deduplication is free. Ten snapshots of an almost-unchanged repo add only a few objects — identical file content is stored once, forever.
  • Restore never rewrites history. It writes files back into your working tree. Committed work is left alone (it's already safe).
  • You can inspect them with plain git: git log refs/gitundo/checkpoints and even push the ref to back your safety net up on a remote.

Guarantees

  • The working tree, index, branch refs and tags are never modified by snapshot / list / diff.
  • Restore never deletes files that exist only in commits newer than the checkpoint, and never deletes untracked files (unless you pass --delete-extraneous).
  • Restore never overwrites a file with local edits — they are parked as *.gitundo-keep and reported (pass --hard to overwrite instead).
  • Snapshot commits are authored gitundo, so they are instantly recognisable in git log and can never be mistaken for (or signed as) your work.
  • Every command degrades gracefully: if gitundo can't run, your git command still runs.

What gitundo deliberately is not

  • Not a backup tool for .git itself, and it does not protect committed history (reflog + remotes already do).
  • Not a replacement for good commits — it protects the stuff between them.

Recipes

Recover from git clean -fdx (deleted untracked work):

gitundo snap            # do this BEFORE you clean
git clean -fdx          # ...or let the auto-guard snapshot for you
# hours later:
gitundo list
gitundo restore 0       # everything is back, as untracked files

Undo a wrong git reset --hard (uncommitted + committed work):

gitundo snap            # before the reset
git reset --hard HEAD~5
gitundo restore         # working tree is exactly what you snapshotted
git reset --hard <your branch tip>   # then optionally re-point your branch

Branch experiments without fear:

gitundo snap exp-start
git checkout -b experiment
# ... go wild ...
git checkout main
gitundo restore exp-start    # clean tree exactly as you left it

Before an AI agent / co-pilot edits your repo:

gitundo snap "before agent run"
# let the agent do its thing; if it trashes files, gitundo restore

In CI / scripts (zero output unless something is wrong):

gitundo snap -t ${GITHUB_SHA} || true     # snapshot, never fail the build

As a library:

from gitundo import core

core.snapshot(path=".", message="deploy preflight", tag="pre-deploy")
cps = core.list_checkpoints()
core.restore(path=".", selector="pre-deploy")

Comparison

Tool What it does How gitundo differs
git stash Temporarily shelves a single change set Checkpoints stack: full history of every intermediate state, browseable & restorable by tag/number
reflog Undoes committed operations gitundo protects uncommitted state, which reflog can't
ugit / gitjk Interactive recipe for reversing specific git commands gitundo is automatic + non-interactive; snapshots include untracked files & deletions
git-snap Manual snapshot to a side branch gitundo: full undo cycle (snap → list → diff → restore), tags, guard, prune
jj / agents' checkpoints Continuous op-log / per-session snapshots gitundo rides on your plain git (no new VCS), opt-in per repo, no config

FAQ

Does it slow git down? Only the commands you ask it to guard, and only for the brief moment a checkpoint is written. Ordinary commands pass straight through.

Where does the data live? In your repo's own .git object store, under refs/gitundo/. There is no daemon, no database, nothing to corrupt independently of git.

Can I push checkpoints to a remote? Yes — they're refs: git push origin refs/gitundo/checkpoints:refs/gitundo/checkpoints.

Is this safe for huge monorepos? Snapshots reuse git's own content addressing, so unchanged content costs almost nothing. Files larger than ~64 MB are skipped for byte-comparison during restore (still snapshotted).

How do I reclaim space? gitundo prune -k 20, then occasionally git gc --prune=now.

Where are my checkpointed files if the working tree is clean? If a snapshot captured nothing new, gitundo says so and stores nothing.


Contributing

Contributions are very welcome — docs, tests, shell guards, integrations (IDE, pre-commit, Git hooks), and more destructive-command patterns. See CONTRIBUTING.md. The codebase is intentionally small:

src/gitundo/core.py   ~ the engine (snapshot, restore, diff, prune, guard)
src/gitundo/cli.py    ~ the command-line interface
tests/                ~ 75+ tests against real throwaway git repositories
extensions/vscode/    ~ the official VS Code extension (sidebar, commands)

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitundo-0.1.0.tar.gz (34.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitundo-0.1.0-py3-none-any.whl (24.9 kB view details)

Uploaded Python 3

File details

Details for the file gitundo-0.1.0.tar.gz.

File metadata

  • Download URL: gitundo-0.1.0.tar.gz
  • Upload date:
  • Size: 34.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gitundo-0.1.0.tar.gz
Algorithm Hash digest
SHA256 99d46b236a8138a72c72fb9073b57c2ff3698ef6e6bf5b0d8a1ee23d7c3588f6
MD5 015f07230bf6859cf0ac96a2b8e88275
BLAKE2b-256 f27ef548a8d5aef79272b5cd7602d0817351d30a81b7995f7c0ceee2e48011dd

See more details on using hashes here.

File details

Details for the file gitundo-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gitundo-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 24.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gitundo-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 32ea6764862ac3ca54a2ca710ab13aec8470e22f89daaa087f1028c2c634481d
MD5 9b71f8088b1a66229a95554b60e0cef0
BLAKE2b-256 012a701b65226abb0e7eb5ef570c74a6603e5dcdc15cc7a4f4007dd2f58f155c

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page