EXPERIMENTAL SOFTWARE, MAY DO DESTRUCTIVE THINGS TO YOUR GIT REPOSITORY. PROBABLY DO NOT USE THIS ON A REPO YOU CARE ABOUT.
git oops
Have you ever made a mistake with git and wished you could just type git undo
instead of having to remember a weird incantation? That's the idea behind git oops
.
$ git rebase -i main
# do something bad here
$ git oops undo
# fixed!
The goal is to experiment and see if it's possible to build a standalone undo
feature for git, in the style of the undo features in GitUp,
jj, and git-branchless.
You can think of it as "version control for your version control" -- it takes snapshots of your repository and makes those into git commits that you can restore later to go back to a previous state.
This is really just a prototype -- I think the idea of a standalone undo
feature for git is cool and I'm mostly putting this out there in case it can
serve as inspiration for a better tool that actually works reliably. There's a
long list of problems at the end of this README and it's not remotely ready for
production use.
installation
- Put the
git-oops
script into your PATH somewhere - Install
pygit2
globally on your system - Run
git oops init
in a repository to install the hooks (danger! will overwrite your existing hooks!) - If you'd like, alias
git undo
togit oops undo
Now git-oops
will automatically take a snapshot any time you do anything in
your Git repo.
basic usage
git oops undo
will undo the last operation.git oops history
shows you the history of all snapshots taken using a curses-based interface and lets you interactively pick one to restore.
advanced usage
git oops record
manually records a snapshot.git oops restore SNAPSHOT_ID
restores a specific snapshot
how it works
when git oops record
takes a snapshot, here's what it does:
- save your staging area and workdir: It creates a commit for your current staging area and working directory (very similarly to how
git stash
does). - get HEAD
- get all your branches and tags
- check for uniqueness: If the snapshot is exactly the same as the previous snapshot, it'll exit
- record everything in a commit. Here's an example commit (from this repository). The metadata is stored in the commit message.
FormatVersion: 1
HEAD: refs/heads/main
Index: 20568a3a49feda34ad6aaa3aff7d7a578a8dee0d
Workdir: 4d1a195dc04ab74cfe1cd94da826ce5b0069d264
Refs:
refs/heads/libgit2: c02fc253375108ec797b6af3ca957e8ea0cc36b9
refs/heads/main: 1b4cdfab2900b3b99473560e76e3f91c560364a0
refs/heads/test: 9ac4a5d8e10b04cdddab698e8a9053e7e645543c
refs/heads/test2: 4247707e426f4b890ecd7314376c4d706a2d799d
- update the git-undo reference: It updates
refs/git-undo
to point at the commit it created in step 5. - update the reflog: it updates the reflog for
refs/git-undo
to include the new commit
More details about other commands:
git oops history
retrieves the history from the reflog forrefs/git-undo
git oops restore COMMIT_ID
:- retrieves COMMIT_ID
- runs
git -c core.hooksPath=/dev/null restore --source WORKDIR_COMMIT
- runs
git -c core.hooksPath=/dev/null restore --staged --source INDEX_COMMIT
- updates all the branches and tags from the snapshot. It will not delete any branches or tags, to avoid deleting their reflog.
git oops history
:- runs the equivalent of
git reflog git-oops
to get a list of histories - gives you an interactive UI to choose one to restore
- runs the equivalent of
git oops undo
:- runs the equivalent of
git reflog git-oops
to get a list of histories - finds the first one where any of your references changed and restores that one
- runs the equivalent of
git oops init
installs the following hooks: post-applypatch, post-checkout, pre-commit, post-commit, post-merge, post-rewrite, post-index-change, reference-transaction- when the hooks run, it runs
git oops record
- when the hooks run, it runs
idea: make it easier to share broken repo states
You could imagine using this to share repository snapshots with your coworkers, like
you run:
$ git oops record
23823fe
$ git branch my-broken-snapshot 23823fe
$ git push my-broken-snapshot
they run:
# make a fresh clone
$ git clone your-repo
$ git fetch origin my-broken-snapshot
$ git oops restore 23823fe
now they can see what weird state you ended up in and help you fix it!
I think this doesn't quite work as is though.
problems
Current problems include:
git oops init
overwrites your git hooks and doesn't give you any way to uninstall them. You need to uninstall it manually- It doesn't really support multiple undos
- there's no preview for what it's going to do so it's kind of scary
- makes all of your commits and rebases slower, in some cases MUCH slower. Maybe Python is not a good choice of language? Not sure.
- it's mostly untested so I don't trust it
- probably a million other things
acknowledgements
People who helped: Kamal Marhubi, Dave Vasilevsky, Marie Flanagan, David Turner
Inspired by GitUp, jj, and git-branchless, if you want an undo feature to actually use one of those tools is a better bet.