Revert to a commit by a SHA hash in Git? [duplicate]
Asked Answered
P

9

746

I'm not clear on how git revert works. For example, I want to revert to a commit six commits behind the head, reverting all the changes in the intermediary commits in between.

Say its SHA hash is 56e05fced214c44a37759efa2dfc25a65d8ae98d. Then why can't I just do something like:

git revert 56e05fced214c44a37759efa2dfc25a65d8ae98d
Pericope answered 12/12, 2009 at 23:34 Comment(4)
Even though this question is actually older than the one it's now marked as a duplicate of, that one has a better answer. meta.stackexchange.com/questions/147643/…Guanaco
This question and the top answer here may confuse git users. Just to help understand the terminology, you don't revert to a commit. You can either reset to a commit (which is like going back in time using time machine) or revert a commit (which is like pulling out a commit as if it never existed - however it does preserve the revert info in history, allowing you to revert a revert if you wanted to) Note also that you shouldn't use the m flag and type a commit message if you get conflicts in the process. The auto message git provides is more informative when looking back in history.Assignable
@alexrogins what does pulling out a commit as if it never existed mean? Not sure what 'revert a revert' refers to either - appreciate the comment though, good info, just looking for more detail on your perspective.Arabelle
@Arabelle as in if you add a line of code then commit that line, if you were to revert it you would be undoing that line of code (wherever it was first written in history, doesn't have to be the last commit). That then makes a revert commit. If you revert that revert commit then you're essentially undoing the undo (i.e. redoing the original line again)Assignable
C
1353

If you want to commit on top of the current HEAD with the exact state at a different commit, undoing all the intermediate commits, then you can use reset to create the correct state of the index to make the commit.

# Reset the index and working tree to the desired tree
# Ensure you have no uncommitted changes that you want to keep
git reset --hard 56e05fced

# Move the branch pointer back to the previous HEAD
git reset --soft "HEAD@{1}"

git commit -m "Revert to 56e05fced"
Chromosome answered 12/12, 2009 at 23:51 Comment(14)
Wouldn't it be equivalent (and one command shorter) to do: git reset --hard 56e05fced as the first command, and then skip the final git reset --hard?Noose
When I did this I ended up with a bunch of Untracked Files in the working tree. However looking at the history I could see that those files did have a corresponding delete commit in that "Revert to SHA" commit. So after git reset --hard at the end, you can do git clean -f -d to clean up any untracked files that lingered about. Also, thank you so much this helped me solve a crisis!Brody
do I have to do the git reset --soft HEAD@{1} unconditionally? I mean always with a value of 1?Gymno
@vemv Yes, unless you want to throw away commits on the tip of the branch. git reset 56e05fced adds another entry to the reflog (run git reflog), so git reset --soft HEAD@{1} simply moves the pointer back to the HEAD prior to calling git reset 56e05fced. Using a higher number (e.g. git reset --soft HEAD@{2}) would append the new commit on a previous commit. That is, increasing the number would essentially throw away N-1 commits where N is the number you replace 1 with.Rigadoon
git reset --soft HEAD@{1} seemed to be an invalid command. My branches have now diverged. This screwed me up badly.Dowery
@Dowery this solutions works. You must have done something wrong, or your reflog might not have any entries in it (if it's a very new clone, for example). Regardless, you'll find more intuitive solutions for "reverting" a branch in this answer.Ordinary
Oh god, git-revert give me the wrong path!! This work!!! I just want EXACTLY (any code, any binary) should same as my previosu commit.Rossetti
as @Brody said, you need to do git clean -f -d to clean up any untracked files, otherwise the process is not fully completed. please update your answerScorpio
@Cupcake please could you change the commit message to say reset to just to avoid confusion with the functionality of git revert which I have described in a comment on the questionAssignable
@Dowery HEAD@{1} should be quoted as 'HEAD{@1}' otherwise, it won't work for me (possibly every zsh users)Puffy
I found this better stackoverflow.com/questions/4114095/… , checkout the commit hash , and then create a new branch from there and work on that branch; so that nothing is missedGrandmother
Have updated so that clean should no longer be needed.Chromosome
He asked about git revert. Your answer says to use git reset. "If git revert is a “safe” way to undo changes, you can think of git reset as the dangerous method." Attalasian Git TutorialMerrymaking
careful with the git reset --hard. You should rather use git checkout <revision-id>. This way you disconnect from the branch.Ratiocinate
S
187

What git-revert does is create a commit which undoes changes made in a given commit, creating a commit which is reverse (well, reciprocal) of a given commit. Therefore

git revert <SHA-1>

should and does work.

If you want to rewind back to a specified commit, and you can do this because this part of history was not yet published, you need to use git-reset, not git-revert:

git reset --hard <SHA-1>

(Note that --hard would make you lose any non-committed changes in the working directory).

Additional Notes

By the way, perhaps it is not obvious, but everywhere where documentation says <commit> or <commit-ish> (or <object>), you can put an SHA-1 identifier (full or shortened) of commit.

Sayyid answered 13/12, 2009 at 9:54 Comment(3)
In the case that you're history has already been pushed to a remote before you did the hard reset, you would need to force push the newly reset branch with git push -f, but Be Warned that this could possibly unintentionally delete other users' commits, and if not delete new commits, then it will force other users to resynchronize their work with the reset branch, so make sure this is OK with your collaborators first.Ordinary
This seems to be the best answer. It also tells clearly the difference between git revert and git reset.Coralline
For simply reverting changes committed (including pushed), using the git revert <SHA-1> in the first part of this answer is the cleanest and easiest solution. It has the benefit of showing a clear history with the additional revert commits showing up as normal too. No need to force push either.Sandhurst
M
95

The best way to rollback to a specific commit is:

git reset --hard <commit-id>

Then:

git push <reponame> -f
Mizell answered 19/11, 2012 at 13:50 Comment(5)
Novices should be aware that push -f can destroy history. However, sometimes this is what you want :)Burnsed
Sometimes you are really glad that the history is deleted...was looking for this -f option, thks !Bloodroot
Thanks, to be literal, I had to type in -> git push origin master -f where <reponame> can't just be origin at least for meMonstrous
As people have mentioned above, If we want our repo head pointing to a specific commit without maintaining history then use above steps other wise we can use git revert.Passade
Is there anyway that we know who had reset (i.e rollback commit) and forced push on a specific branch?Champagne
C
90

It reverts the said commit, that is, adds the commit opposite to it. If you want to checkout an earlier revision, you do:

git checkout 56e05fced214c44a37759efa2dfc25a65d8ae98d
Crackerjack answered 12/12, 2009 at 23:40 Comment(4)
then I can just merge this with the head? What if I anticipate having TONS of conflicts, can I just force this commit to be the head "as-is" and just overwrite any conflicts?Pericope
I'm not sure what head you're talking about. You can just move your head back to this commit. (for instance by deleting and creating branch). If you want to do a "merge" commit into the head, which is effectively the reversal of the intermediate commits, you can use merge with "ours" strategy. Pick your option and read manpages. The power is waiting for you to use it ;-)Crackerjack
That makes sense, the reason I ask is that git now tells me that I'm not on any branch.Pericope
because you aren't. if you type git branch you will clearly see it. You can do for instance git checkout -b mybranch 56e05 to get it with branch.Crackerjack
A
82

Updated:

If there were no merge commits in between, this answer provides a is simpler method: https://mcmap.net/q/11819/-how-do-i-revert-a-git-repository-to-a-previous-commit

But if there was one or more merge commits, that answer won't work, so stick to this one (that works in all cases).

Original answer:

# Create a backup of master branch
git branch backup_master

# Point master to '56e05fce' and
# make working directory the same with '56e05fce'
git reset --hard 56e05fce

# Point master back to 'backup_master' and
# leave working directory the same with '56e05fce'.
git reset --soft backup_master

# Now working directory is the same '56e05fce' and
# master points to the original revision. Then we create a commit.
git commit -a -m "Revert to 56e05fce"

# Delete unused branch
git branch -d backup_master

The two commands git reset --hard and git reset --soft are magic here. The first one changes the working directory, but it also changes head (the current branch) too. We fix the head by the second one.

Archaimbaud answered 22/3, 2013 at 5:9 Comment(10)
The -a in your commit isn't necessary.Erastian
Perfect. This should become one single command in the git cli, IMO.Upsurge
very nice! this is much better than git revert 56e05fce..HEAD because it's just one commitGlendaglenden
mmm, I take that back, this is simpler: stackoverflow.com/questions/4114095/…Glendaglenden
@Glendaglenden actually no, the link you gave is not simpler, even though it has thousands of upvotes. The reason is that it simply doesn't work if there is one or more merge commits in the range, which often happens. This one should be indeed the top answer.Pursy
Thank you so so much. git revert with merges was killing me when I just want the content of a previous commitTensity
Note: In the presence of git submodules, it is neccessary to git submodule update --init --recursive after git reset --hard 56e05fce.Bucolic
I made it into a git alias: dpc.pw/…Bucolic
Worked! Except now my CLI says the branch is (dev | REVERTING). @jacob-dam, what is the matter now?Spheroid
I don't really get why the two reset commands. Surely you want to reset and force push ( I know the force push is super destructive) but it's not clear to me what steps 3-5 actually do and why is different to hard reset and force push?Milk
O
79

If your changes have already been pushed to a public, shared remote, and you want to revert all commits between HEAD and <sha-id>, then you can pass a commit range to git revert,

git revert 56e05f..HEAD

and it will revert all commits between 56e05f and HEAD (excluding the start point of the range, 56e05f).

Osmometer answered 19/12, 2011 at 13:15 Comment(5)
Note that if you're reverting a few hundred commits, this could take a while because you have to commit each revert individually.Erastian
@Erastian you don't have to revert each commit individually, you can either pass the --no-edit option to avoid having to make individual commit messages, or you can use --no-commit to commit the reversions all at once.Ordinary
@Cupcake you are right HEAD..56e05f doesn't work for me but 56e05f..HEAD did the trickAlyce
This is by far my preferred way of rolling back, no matter if you pushed it or not. I added this to my global ~/.gitconfig under the aliases section: rollback = "!git revert --no-commit $1..HEAD #" - so now I can just intuitively do $ git rollback a1s2d3Minnick
This seems super close to what I want, but I had about 30 commits to revert, but about half way through it fails on a merge commit with error: Commit 6b3d9b3e05a9cd9fc1dbbebdd170bf083de02519 is a merge but no -m option was given. fatal: revert failed - any suggestions? I tried adding -m but wasn't quite sure how that would work with thisMiso
G
6

This is more understandable:

git checkout 56e05fced -- .
git add .
git commit -m 'Revert to 56e05fced'

And to prove that it worked:

git diff 56e05fced
Ginni answered 23/6, 2011 at 16:29 Comment(5)
This isn't correct in general, I'm afraid. The checkout will only (I think) update paths that exist, so if a file has been deleted since 56e05fced, it won't be staged by doing git checkout 56e05fced -- .Noose
This solution won't delete new files that have been added since 56e05fced , like a git reset --hard or a git revert would. You really want to use those commands if you actually want to restore the state of 56e05fced, not git checkout.Ordinary
Note: This will put you in a detached head state. Not advisable!Assignable
This worked for me, but what does "--" mean in this context?Antiquarian
In Bash like commands, double-dashes means the end of command options, so any value added after double-dashes wont be interpreted as an option. In this context the dot (.) wont be interpreted as an option, instead it will act as the <pathspec> of checkout command.Vintner
B
3

Should be as simple as:

git reset --hard 56e05f

That'll get you back to that specific point in time.

Babu answered 4/1, 2013 at 0:15 Comment(1)
...and is also very dangerous as will wipe all the history since including other peoples work. Beware of this one!Assignable
S
-2

This might work:

git checkout 56e05f
echo ref: refs/heads/master > .git/HEAD
git commit
Sendal answered 25/6, 2012 at 22:36 Comment(1)
This basically does the same thing as git reset --hard 56e05f, except this is less safe and more hacky. You might as well use Charle's solution or Jakub's solution.Ordinary

© 2022 - 2024 — McMap. All rights reserved.