> why is there no git undo command shipped in box? There is absolutely no technical barrier to shipping such a command.
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...