For example: a number of operations are not reversible in the current git model because they're not tracked. We'd need git to start tracking uncommitted changes so that it can restore them on `undo`. That's a fundamental change to how git works and I doubt that the idea would be entertained
No? If an operation is not reversible then it’s not undoable. That’s not abnormal. The opposite really.
git reset —hard HEAD~1
It is already part of the CLI API.Or it could use the ref log (possibly augmented) as a literal undo log, and follow that.
> That would break a lot of the guarantees gained from the append only graph that git exploits for nearly everything it does.
The graph is really not append-only.
The commit graph is effectively append-only, but the named pointers into that graph can be reassigned at will, which means some commits can become (more or less) unreachable and eligible for garbage collection.
Sound to me like the commit graph is not append-only.
Yes, it is. You can't change a commit: you have to create a new one, and create copies of any child commits, which are also different commits. This generates a different graph. What changes are things like tags, branches and HEAD, but they're just pointers, not part of the graph itself.
> Yes, it is. You can't change a commit
You can remove commits from the graph. That’s not append only in my book.
In the same way you can’t rollback a db rollback, you can’t undo a git gc which cleaned up a bunch of unreachable objects.
edit: there's even prior art. Sapling has undo: https://sapling-scm.com/docs/overview/undo/ and is GPLv2 licensed. Just look at what Sapling does.
Technically nothing prevents the database automatically creating a subtransaction / savepoint for every operation you perform.
> This command would have to decide what to do for every previous command, no?
Yes? Not sure I see what the issue with that is.
And technically much of the information is already encoded in the reflog. The two big additional informations you’d need are:
1. Tracking changes to refs as they’re not necessarily tracked by the reflog, but there should only be a small number of plumbing operations manipulating refs so it’s not really a concern.
2. Tracking changes to the index, would likely be a lot more difficult.
The issue is that it sounds even more confusing in practice than status quo.
If you don't understand git, you can't rely on `git undo` cause you don't know what's undoable and what isn't. And if you do understand git, you don't need `git undo` in the first place, you can already undo things by yourself.
To undo something you gotta know what thing-that-you-did to undo in the first place.
This is often achieved using the command design pattern, but git does not record commands, nor has such a concept. It only has state.
Let's imagine it does record commands, what should a purported magical "undo" right after each one of these commands?
- git branch topic
- git branch -f topic cafebead
- git checkout topic
- git checkout cafebead
- git checkout -- file
- git commit # detached
- git commit # on branch
- git commit --amend
- git reset HEAD^
- git reset --hard HEAD^
- git add foo
- git rm foo
- git rm --cached bar
- git merge a b c # merge succeeds
- git merge a b c # merge fails
- git merge a b c # merge fails, resolve conflicts, add, continue
- git merge --abort
- git cherry-pick
- git cherry-pick --abort
- git rebase main
- git rebase --onto main cafebead^ topic
- git rebase -i # pick, reword, drop commits, one fails
- git rebase -i # pick, reword, drop commits, one fails, fix, add, continue
- git rebase --abort
- git stash
- git fetch
- git pull
You could come up with answers to that, but then, they would make sense only with one use case of what you intended to do with each command. To cater for that you'd start to need "git undo --this-way" or "--that-way". Any command that cannot be undone would break undo. Any external tool would need to operate using only commands, or it would break undo. Any external too or script extending git by chaining commands would need to implement new commands with undo for it not to break undo (otherwise "git undo" would only undo the last one). Also, consider that there may be files untracked in the tree and tracked in other branches, become tracked midway through a merge or rebase and whatnot. It becomes stupendously complex stupendously fast.If "git undo" were made for beginner users to make it more approachable, then it would fail as soon as the user faced a non-undoable situation and not understand why ("this undo is stupid!"). If "git undo" were made for advanced users to make it more convenient, then it would fail as soon as the user used advanced tools or scripts breaking undo.
What git chose to do is to be the simplest possible tool: it's a DAG made out of commits that can be labeled, and its commands operate on the DAG and labels (with a staging area so that commit creation is transactional). Commits are not removed until they're GC'd and "undoing" is moving back labels to be pointing to commits as they were before, so not having "git undo" is not even dangerous.
To make "git undo" robust, git would need to hide and/or change its core design + API + UI, which would make it not-git. I'd argue that part of the success of git is these "internals" (which are its actual interface) made it spectacularly easy to script, extend, compose, write alternative implementations of...
The only way to implement "git undo" is to embrace the database-like design (stage manipulation with git add and git rm are preparing a transaction that gets committed with git commit) and explicitly start (git start / git begin, records current work tree + refs + index state) and end (git end, a.k.a discard recorded state) or rollback (git undo / git rollback, restore recorded state).
BEGIN TRANSACTION git begin
INSERT INTO ... echo >> foo && git add foo && git commit
UPDATE ... WHERE git cherry-pick bar
DELETE ... (SELECT ...) git rm baz && git commit
COMMIT // or ROLLBACK git end # or git rollback
Now you gave me some ideas... hold my beer...I don't think so. Lots of software products that support "undo" support undoing across a greater many actions than git provides, and yet they are able to perform the undo just fine; they don't need a special "undo --paint" and "undo --move-object" and "undo --reset-to-default".
There's literally nothing special about what git does that can't be covered by a general "undo" command which looks at the last git command executed in the local repository and maps that specific intention to an action.
When this is not possible (in the middle of a rebase, for example), simply tell the user (as git currently does) that $ACTION is not possible while performing a rebase.
Having an undo command is likely the most leveraged thing by far the git developers can do to make new users unafraid of it. Screw up somehow? Just undo.
All of those need you to know what you’re doing and what exactly you want to undo
* git branch hidden current
* git do-the-command-here
* if we call git undo, checkout the hidden branch, otherwise delete it
In practice, I think a lot of people call `git lg` often enough that they can just git reset --hard to a recent commit hash, or they manually create a just-in-case branch if they're going to do something risky/dangerous.
Microsoft Word has undo. Git can as well.