Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

git-undo

Undo any Git operation.

$ git rebase -i main            # oops
$ git undo
Restored #41 (2 minutes ago): commit: Add the parser
  refs/heads/feature: 9e1c3a0 -> 4b7d2f1
  files: 3 changed
Undo this with `git undo redo`.

git-undo records a snapshot of your repository after every Git command: where every ref points, HEAD, the index (conflicts included), the working tree (untracked files included) and the stash. Each snapshot is ordinary Git data, stored under refs/undo/ in the repository itself. git undo moves the repository back to the previous snapshot, git undo redo forward again, and git undo restore <n> to any of them. Refs move in one atomic transaction; files that changed since the last snapshot are never overwritten.

Git's reflog already lets you find where a branch used to point. It does not know what was in your index, which untracked files existed, what a stash contained before you dropped it, or how to put all of it back at once. git-undo does.

Install

Requires Git 2.31 or newer and, for automatic snapshots, 2.28 or newer.

cargo install git-undo

Or download a binary from the releases page and put git-undo on your PATH. Git finds any git-<name> executable, so it runs as git undo.

Then, once per repository:

git undo init

This writes two hooks (reference-transaction and post-index-change, keeping any hooks already there) that ask git-undo to record a snapshot once the command has settled. Git never waits for it. git undo init -D removes the hooks again; add --purge to delete the snapshots too.

Use

Command What it does
git undo Back to the state before the last operation.
git undo 3 Back three operations.
git undo redo Forward again.
git undo list The snapshots, newest first; @ marks where you are.
git undo show 41 What #41 recorded and how it differs from #40.
git undo diff 41 Diff #41's files against the working tree now.
git undo restore 41 Return to #41.
git undo restore 41 --refs-only Move refs and HEAD only; leave files alone.
git undo snapshot -m "before the big rebase" Record now, with a note.
git undo watch Record as files change outside Git (editors, agents).
git undo --dry-run Show what an undo would change.
git undo gc Forget snapshots older than undo.expire (90 days).
git undo status Hooks, snapshot count, settings.

Consecutive undos walk further back, including through earlier undos, the way an operation log does; redo walks forward. New work after an undo starts a new line of history: git undo then undoes that work, and there is nothing to redo.

A stopped rebase, merge or cherry-pick blocks undo until you finish or abort it, or pass --force to discard it.

Scripting

git undo list --format='%(seq)%09%(time)%09%(kind)%09%(head)%09%(summary)' -z

Placeholders follow git for-each-ref: %(seq), %(sha), %(short), %(tree), %(time), %(date), %(relative), %(kind), %(summary), %(head), %(restored), %(from), %(mark). -z terminates records with NUL. Exit status is 0 on success and 1 on any failure, with the reason on stderr prefixed fatal:.

Tools that already watch a repository (git-sim's live mode, for one) can call git undo snapshot -q after each change they observe and offer git undo restore <n> for any point on their timeline.

What it costs

A snapshot is a commit whose tree shares every unchanged blob and subtree with the previous one, so recording after each command costs about what git add -A on the changed files costs, in a detached process. Snapshots keep the commits they point at alive, so git gc will not prune a branch you deleted until its snapshots are gone (undo.limit, default 1000 per worktree, or git undo gc). Refs under refs/undo/ show up in git log --all; no porcelain command otherwise sees them.

Configuration

Key Default Meaning
undo.worktree true Record the working tree. false records refs and index only.
undo.settle 700 Milliseconds of quiet before a hook-triggered snapshot.
undo.limit 1000 Snapshots kept per worktree.
undo.expire 90d Age after which git undo gc forgets snapshots.

Design

The snapshot format and restore algorithm are specified in docs/spec.md, precisely enough for another implementation to read and write the same data. The tool is written in Rust and runs every repository operation through the user's own git, so its semantics are exactly those of the installed Git version. There are no bindings, no database and no daemon: remove refs/undo/ and the hooks and nothing is left.

Limits

  • Objects pruned after their snapshot was forgotten cannot come back.
  • Remotes are never touched; restoring refs/remotes/* changes only what the local repository believes.
  • Configuration, hooks and submodule working trees are outside the state.
  • A restore does not re-create a stopped rebase or merge; it records that one was in progress.

License

GPL-2.0, the same license as Git.

About

Undo any Git operation. Written in Rust.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages