Menu for Per-Arne's Git Good!
Courses and project Per-Arne's Git Good!

Undo shared history: git revert

Module 4: Undo and rewrite · 11 min

The interactive lesson needs JavaScript. The text below is the complete lesson.

After this lesson you can

  • undo a pushed commit with git revert and share the fix without force-pushing apply
  • revert a specific older commit and keep the commits that came after it apply
  • explain why reset causes trouble in shared history, and what revert does not hide understand
  • choose between restore, reset and revert from where the change lives evaluate

Understand: Undo by adding

Commit B doubled all the prices, and that was a mistake. Both B and C are pushed, and Ada has pulled them. How do you undo B when others already have it?

Reset looks tempting. But then you only move your own main back to A. The server and Ada still have B and C. Git rejects your next push, and if you force it through, the good commit C disappears from the server too.

Revert does the opposite of removing. It adds a new commit, R, that undoes the change in B. Nothing disappears. History just gets longer.

What is in R? Exactly the opposite change of B. The price goes from 60 back to 30. The opening hours from C stay, because R undoes only B.

Because R builds on top of C, you push it normally. Ada gets R with an ordinary pull, without conflicts and without force.

You can revert an older commit too, not just the newest one. If later commits changed the same lines, you may get a conflict. You resolve it as in a merge, and then you finish with git revert --continue.

Here is the decision rule for all undoing. If the change is not committed, use restore. If it is committed but not shared, you may use reset. If it is shared, use revert. And remember: revert fixes the content, but does not hide the old commit.

Watch: Undo a pushed price change

First we find the guilty commit. Notice that origin/main is on the same commit as main. Everything is pushed, so reset is out of the question.

$ git log --oneline --decorate

The bad commit is the second newest, one before HEAD. We revert exactly that one. --no-edit keeps the message Git suggests, so no editor opens.

$ git revert --no-edit HEAD~1

Let's look at what the new commit contains. The line with 60 is removed, and the line with 30 comes back. That is B turned upside down.

$ git show HEAD

Now we share the fix. The push goes straight through, because we only added a commit on top of what the server already has.

$ git push

Finally Ada pulls. It is a simple fast-forward. No conflict, and no history she already has disappears.

$ git pull --no-rebase

Tasks

  1. Find the commit that doubled the prices, and check whether it is pushed.
  2. Undo the price change with a new commit, and keep the opening hours.
  3. Share the fix with the team.

Practise: Transfer: three problems, three tools

Before each task, ask yourself whether the change lives only in files, in a local commit or in a shared commit. Use git status and git log --oneline to find out.

Tasks

  1. menu.txt has some junk that was never staged. Throw it away.
  2. The newest commit, Quick test, was a mistake. Remove it from main's history, and remove its change from the files too.
  3. The commit Remove tea was a mistake. Put tea back on the menu, and share the fix with the team.

Check yourself: Recall it

A commit you pushed yesterday has broken the build. The team has already pulled it. What do you do?

  • git reset --hard HEAD~1 and then force-push
  • git revert on the commit, and then git push
  • git restore on the files, without committing

You revert a commit three steps back, and Git stops with CONFLICT in prices.txt. What do you do?

  • Run git revert --continue straight away
  • Fix prices.txt, run git add prices.txt, then git revert --continue
  • git reset --hard HEAD~3, so you avoid the conflict

You accidentally pushed a commit with a password. Does git revert make the password safe again?

  • Yes, revert removes the password from history
  • No, the password is still in the old commit, so you must change it
  • Yes, as long as nobody has pulled yet

Review from “Fetch, pull and push”: git push is rejected because Ada pushed first. What is the right next step?

  • git push --force, so your version goes up
  • Get her work with git fetch, integrate it with a merge or rebase, and push again
  • git reset --hard origin/main, and push again

Common misconceptions

  • “Revert deletes the commit.” Revert adds a new commit with the opposite change. The old commit stays in history.
  • “After a revert a leaked password is gone.” The password is still in the old commit. Change the password.
  • “Reset and force-push is the normal way to undo pushed work.” It rewrites history others already have, and causes trouble for the whole team.

Read more

What revert does, precisely

git revert <commit> works out the change that commit made compared with its parent, applies the opposite change to your current HEAD, and commits the result. The old commit stays where it is. The new commit gets a message that names it:

$ git revert --no-edit HEAD~1
[main 890a669] Revert "Double all prices"
 Date: Tue Oct 13 09:14:35 2026 +0200
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git show -s --format=%B HEAD
Revert "Double all prices"
This reverts commit 54235c6e205c2e2cfa55a5517d06f4c6d1c03532.

Revert moves your branch forward by one new commit, just like git commit. It never removes anything, so the result can be pushed normally and pulled by everyone without force. Your staging area and working tree should be clean before you start, because Git needs them to build the new commit.

Useful options:

  • --no-edit keeps the suggested message instead of opening an editor.
  • -n (--no-commit) applies the opposite change to the staging area and working tree, but does not commit. You can then combine several reverts, or adjust the result, in one commit of your own.
  • Several commits or a range are reverted one at a time, newest first. git revert --no-edit HEAD~2..HEAD makes one revert commit for each of the two newest commits.

When revert meets a conflict

If later commits changed the same lines, Git cannot apply the opposite change automatically, and stops in the middle, just like a merge:

$ git revert --no-edit HEAD~1
Auto-merging p.txt
CONFLICT (content): Merge conflict in p.txt
error: could not revert c228a05... Double prices

Open the file, decide what the content should be, then git add p.txt and git revert --continue. git revert --abort takes you back to the state before you started, and git revert --skip skips the commit when you revert several.

Reverting a merge commit: -m 1

A merge commit has two parents, so "the change this commit made" depends on which parent you compare with. Git refuses to guess:

$ git revert HEAD
error: commit 9b1a80256199feb0cf5f16d62319782869b67874 is a merge but no -m option was given.
fatal: revert failed

-m 1 says that parent number 1, the branch you were on when you merged (usually main), is the mainline to keep. The revert then removes everything the merged branch brought in:

$ git revert --no-edit -m 1 HEAD
[main 14a1912] Revert "Merge branch 'feature'"

There is a trap. The content is gone, but the history still says the branch is merged. If you later fix the branch and merge it again, Git only brings in the new commits on the branch, not the ones you reverted. The fix is to revert the revert first, which brings the original changes back, and then merge the new work.

Reverting a revert

A revert commit is an ordinary commit, so it can be reverted too. That is the clean way to undo an undo, for example when the "broken" commit turned out to be fine after all. Recent Git versions call this commit Reapply; older ones write Revert "Revert ...". History now tells the whole story: the change, its undo, and its return.

What revert does not do

Revert makes a new commit that undoes the content of the change. It does not hide the old commit. Anyone can still run git show on it, and every clone and fork still contains it. If you committed a password or an API key, revert is not enough: treat the secret as leaked and change it at once. Removing it from all history requires rewriting every commit since, a forced push and every teammate re-cloning. That is a bigger operation than this lesson covers.

Restore, reset or revert?

First ask whether the change is committed. If it is not, use restore. If it is, ask whether it is pushed or shared. If not, reset is allowed. If it is, use revert.

You want to undo… Where it lives Tool Moves a branch? Can lose work?
an edit you have not staged working tree git restore app.txt no yes, the unstaged edit
a file you staged too early staging area git restore --staged app.txt no no
a file's content, back to an older version a commit git restore --source=HEAD~1 app.txt no yes, the current working-file version
the last commit, keeping the change staged unpushed commit git reset --soft HEAD~1 yes no
the last commit, keeping the change in the files unpushed commit git reset HEAD~1 yes no
the last commit and its change unpushed commit git reset --hard HEAD~1 yes yes, uncommitted edits
a commit others already have pushed commit git revert <commit> yes, forward by one new commit no

Official documentation