restore vs reset vs revert vs rebase
Intermediate Core Git
Why this matters
These four names are the reason Git has a reputation for being confusing. They sound interchangeable and are not: two of them rewrite history and two do not, and choosing wrongly on a shared branch is how a quiet afternoon becomes a team-wide problem.
The one-line answers
| Command | In one sentence |
|---|---|
git restore |
Put a file back the way it was; nothing about history changes |
git reset |
Move the branch pointer to another commit, optionally taking the files with it |
git revert |
Add a new commit that undoes an old one, leaving both in the history |
git rebase |
Replay your commits on top of a different base, giving them new hashes |
What each one touches
Recall the three areas from lesson 2.2: the working tree, the staging area, and the commit history.
| Working tree | Staging area | History | Safe when pushed? | |
|---|---|---|---|---|
git restore <file> |
Overwritten | Untouched | Untouched | Yes, it is local only |
git restore --staged <file> |
Untouched | Unstaged | Untouched | Yes |
git reset --soft |
Untouched | Gets the change | Branch moves | No |
git reset --mixed |
Keeps the change | Cleared | Branch moves | No |
git reset --hard |
Overwritten | Cleared | Branch moves | No |
git revert <hash> |
Updated by the new commit | Used briefly | Adds a commit | Yes |
git rebase <base> |
Rebuilt | Used during replay | Commits replaced | No |
Read the last column as the decision. Anything that says no is a command for work that only exists on your computer.
Reset or revert: the pair that matters
Both undo a commit. The difference is what the history looks like afterwards.
git reset moves the branch back, so the commit is simply not there any more:
before A ─ B ─ C (main)
after A ─ B (main) C is unreferencedgit revert adds a commit whose content is the opposite of the one you name:
before A ─ B ─ C (main)
after A ─ B ─ C ─ C' (main) C' undoes CIf C has been pushed, your colleagues already have it. Reset makes your branch disagree with theirs, and the only way to publish that is a force push, which rewrites the shared branch under them (lesson 12.8). Revert is an ordinary commit that everyone receives with an ordinary pull.
Rebase belongs here too
git rebase is not an undo command, but it appears in this list because it is the fourth thing that changes commits (lesson 6.7). It replays your commits on a new base, and replaying means new hashes, which means the old commits are gone in the same way a reset leaves them behind.
The rule is the same and is worth stating as one: never rewrite commits that other people have. Rebasing your own unpushed branch onto an updated main is routine and encouraged. Rebasing a shared branch is the thing teams write rules about.
Restore is the odd one out
git restore never touches history at all. It is a file command: take the version from the commit (or from the staging area) and write it into the working tree.
That makes it the safest of the four for other people and the most dangerous for you, because the edit it discards was never recorded anywhere. The reflog cannot bring back what was never committed (lesson 12.1).
Choosing, in practice
| Situation | Command |
|---|---|
| "This file is a mess, give me the committed version" | git restore <file> |
| "I staged the wrong file" | git restore --staged <file> |
| "My last three commits should be one, and nothing is pushed" | git reset --soft HEAD~3, then commit again |
"That commit should never have gone in, and it is on main" |
git revert <hash> |
"My branch is behind main and I want a straight history" |
git rebase main, on your own branch |
| "I have no idea what I did" | git reflog first, then decide |
How to do it
The four commands, with the safety question attached:
$ git restore <file> # discards your edit permanently
$ git restore --staged <file> # unstages, keeps the edit
$ git reset --soft HEAD~1 # local only
$ git revert <hash> # safe anywheregit status before and git log --oneline -3 after: those two habits catch nearly every mistake in this lesson.
Discard Changes is restore, the minus button is restore --staged, Undo Last Commit is a soft reset, and Revert Commit in a commit's context menu is revert. VS Code asks for confirmation before the destructive one, which is Discard.
Rollback is restore. In the Log, right-click a commit for Revert Commit, Undo Commit and Reset Current Branch to Here…, where a dropdown chooses the reset mode with an explanation of each.
The platform offers only the safe one: Revert on a merged merge request or on a commit page, which creates a revert commit and a merge request for it. Resetting a shared branch is not something the interface will do for you, which is a deliberate choice.
The same: Revert on a merged pull request opens a new pull request that undoes it.
Common mistakes
- Resetting a pushed branch, then force pushing to "fix" it, and rewriting history under three colleagues.
- Confusing
git restore <file>withgit restore --staged <file>. - Reverting a merge commit without
-m, which fails with a message about a mainline; that is lesson 12.6. - Rebasing a shared branch to tidy it up.
- Thinking
revertmeans "go back to that commit". It undoes one commit, it does not restore an old state.
Try it yourself
Goal: see the two histories side by side.
- On a branch, make commits
A,BandC(any small edits). - Run
git log --oneline -4and note the hashes. - Run
git reset --hard HEAD~1and look at the log:Cis gone. - Bring it back with
git reset --hard ORIG_HEAD. - Now run
git revert HEADand look at the log:Cis still there, with a new commit above it. - Compare the two logs and write down which one a colleague would have to reconcile.
Expected result: two ways of removing the same change, one of which leaves a history that no longer matches anyone else's.
Show solution
Step 6 is the answer to the whole lesson. After the reset, your main and your colleague's main have different commits with the same name, which Git can only resolve with a force push and a lot of communication. After the revert, they run git pull and are done.