How do I revert a Git repository to a previous commit?
Solution 1
This depends a lot on what you mean by "revert".
Temporarily switch to a different commit
If you want to temporarily go back to it, fool around, then come back to where you are, all you have to do is check out the desired commit:
# This will detach your HEAD, that is, leave you with no branch checked out:
git checkout 0d1d7fc32
Or if you want to make commits while you're there, go ahead and make a new branch while you're at it:
git checkout -b old-state 0d1d7fc32
To go back to where you were, just check out the branch you were on again. (If you've made changes, as always when switching branches, you'll have to deal with them as appropriate. You could reset to throw them away; you could stash, checkout, stash pop to take them with you; you could commit them to a branch there if you want a branch there.)
Hard delete unpublished commits
If, on the other hand, you want to really get rid of everything you've done since then, there are two possibilities. One, if you haven't published any of these commits, simply reset:
# This will destroy any local modifications. # Don't do it if you have uncommitted work you want to keep. git reset --hard 0d1d7fc32
Alternatively, if there’s work to keep:
git stash git reset —hard 0d1d7fc32 git stash pop
This saves the modifications, then reapplies that patch after resetting.
You could get merge conflicts, if you’ve modified things which were
changed since the commit you reset to.
If you mess up, you've already thrown away your local changes, but you can at least get back to where you were before by resetting again.
Undo published commits with new commits
On the other hand, if you've published the work, you probably don't want to reset the branch, since that's effectively rewriting history. In that case, you could indeed revert the commits. In many enterprise organisations, the concept of "protected" branches will even prevent history from being rewritten on some major branches. In this case, reverting is your only option.
With Git, revert has a very specific meaning: create a commit with the reverse patch to cancel it out. This way you don't rewrite any history.
First figure out what commits to revert. Depending on the technique chosen below, you want to either revert only the merge commits, or only the non-merge commits.
# This lists all merge commits between 0d1d7fc and HEAD:
git log --merges --pretty=format:"%h" 0d1d7fc..HEAD | tr '\n' ' '
# This lists all non merge commits between 0d1d7fc and HEAD:
git log --no-merges --pretty=format:"%h" 0d1d7fc..HEAD | tr '\n' ' '
Note: if you revert multiple commits, the order matters. Start with the most recent commit.
# This will create three separate revert commits, use non merge commits only: git revert a867b4af 25eee4ca 0766c053
It also takes ranges. This will revert the last two commits:
git revert HEAD~2..HEAD
Similarly, you can revert a range of commits using commit hashes (non inclusive of first hash):
git revert 0d1d7fc..a867b4a
Reverting a merge commit. You can also use a range of merge commits here.
git revert -m 1 <merge_commit_sha>
To get just one, you could use
rebase -i
to squash them afterwardsOr, you could do it manually (be sure to do this at top level of the repo)
get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .
Then commit. Be sure and write a good message describing what you just did
git commit
The git-revert
manpage actually covers a lot of this in its description. Another useful link is this git-scm.com section discussing git-revert.
If you decide you didn't want to revert after all, you can revert the revert (as described here) or reset back to before the revert (see the previous section).
You may also find this answer helpful in this case:
How can I move HEAD back to a previous location? (Detached head) & Undo commits
Solution 2
Lots of complicated and dangerous answers here, but it's actually easy:
git revert --no-commit 0d1d7fc3..HEAD
git commit
This will revert everything from the HEAD back to the commit hash (excluded), meaning it will recreate that commit state in the working tree as if every commit after 0d1d7fc3
had been walked back. You can then commit the current tree, and it will create a brand new commit essentially equivalent to the commit you "reverted" to.
(The --no-commit
flag lets git revert all the commits at once- otherwise you'll be prompted for a message for each commit in the range, littering your history with unnecessary new commits.)
This is a safe and easy way to rollback to a previous state. No history is destroyed, so it can be used for commits that have already been made public.
Note on merge commits:
If one of the commits between 0766c053..HEAD (inclusive) is a merge then there will be an error popping up (to do with no -m specified). The following link may help those encountering that: Why does git revert complain about a missing -m option? (thanks @timhc22 for pointing out)
Solution 3
Rogue Coder?
Working on your own and just want it to work? Follow these instructions below, they’ve worked reliably for me and many others for years.
Working with others? Git is complicated. Read the comments below this answer, consider other answers, and discuss with your team before you do something rash.
Reverting Working Copy to Most Recent Commit
To revert to the previous commit, ignoring any changes:
git reset --hard HEAD
where HEAD is the last commit in your current branch
Reverting The Working Copy to an Older Commit
To revert to a commit that's older than the most recent commit:
# Resets index to former commit; replace '56e05fced' with your commit code git reset 56e05fced
Moves pointer back to previous HEAD
git reset —soft HEAD@{1}
git commit -m “Revert to 56e05fced”
Updates working copy to reflect the new commit
git reset —hard
Push your changes to respective branch
git push -f
Credits go to a similar Stack Overflow question, Revert to a commit by a SHA hash in Git?.
Solution 4
The best option for me and probably others is the Git reset option:
git reset --hard <commitId> && git clean -f
This has been the best option for me! It is simple, fast and effective!
** Note:** As mentioned in comments don't do this if you're sharing your branch with other people who have copies of the old commits
Also from the comments, if you wanted a less 'ballzy' method you could use
git clean -i
NOTE : This is getting a lot of attention. It's super important you do not do this if you're working on a branch which other people working from, also (depending on time frame) not one remote which has been merged with master. You will run into git hell.
Solution 5
Before answering let's add some background, explaining what this HEAD
is.
First of all what is HEAD?
HEAD
is simply a reference to the current commit (latest) on the current branch. There can only be a single HEAD
at any given time (excluding git worktree
).
The content of HEAD
is stored inside .git/HEAD
, and it contains the 40-bytes SHA-1 hash of the current commit.
detached HEAD
If you are not on the latest commit - meaning that HEAD
is pointing to a prior commit in history it's called detached HEAD
.
On the command-line it will look like this - SHA-1 hash instead of the branch name since the HEAD
is not pointing to the the tip of the current branch:
A few options on how to recover from a detached HEAD:
git checkout
git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back
This will checkout new branch pointing to the desired commit. This command will checkout to a given commit.
At this point you can create a branch and start to work from this point on:
# Checkout a given commit. # Doing so will result in a `detached HEAD` which mean that the `HEAD` # is not pointing to the latest so you will need to checkout branch # in order to be able to update the code. git checkout <commit-id>
Create a new branch forked to the given commit
git checkout -b <branch name>
git reflog
You can always use the reflog
as well. git reflog
will display any change which updated the HEAD
and checking out the desired reflog entry will set the HEAD
back to this commit.
Every time the HEAD is modified there will be a new entry in the reflog
git reflog
git checkout HEAD@{...}
This will get you back to your desired commit.
git reset HEAD --hard <commit_id>
"Move" your HEAD
back to the desired commit.
# This will destroy any local modifications. # Don't do it if you have uncommitted work you want to keep. git reset --hard 0d1d7fc32
Alternatively, if there’s work to keep:
git stash git reset —hard 0d1d7fc32 git stash pop
This saves the modifications, then reapplies that patch after resetting.
You could get merge conflicts, if you’ve modified things which were
changed since the commit you reset to.
- Note: (Since Git 2.7) you can also use the
git rebase --no-autostash
as well.
This schema illustrates which command does what. As you can see there reset && checkout
modify the HEAD
.