1406

I have a project in a remote repository, synchronized with a local repository (development) and the server one (prod). I've been making some commited changes already pushed to remote and pulled from the server. Now, I want to undo those changes. So I could just git checkout to the commit before the changes and commit the new changes, but I'm guessing that there will be problems to push them again to remote. Any suggestion on how should I proceed?

Liam
  • 27,717
  • 28
  • 128
  • 190
Manolo
  • 24,020
  • 20
  • 85
  • 130

14 Answers14

1630

You can revert individual commits with:

git revert <commit_hash>

This will create a new commit which reverts the changes of the commit you specified. Note that it only reverts that specific commit, and not commits that come after that. If you want to revert a range of commits, you can do it like this:

git revert <oldest_commit_hash>..<latest_commit_hash>

It reverts all the commits after <oldest_commit_hash> up to and including <latest_commit_hash>. On some versions of git it also reverts the <oldest_commit_hash>, so double check if that commit gets reverted or not. You can always drop the latest revert commit (which reverts the oldest commit) with g reset --hard HEAD~.

To find out the hash of the commit(s) you can use git log.

Look at the git-revert man page for more information about the git revert command. Also, look at this answer for more information about reverting commits.

gitaarik
  • 42,736
  • 12
  • 98
  • 105
  • 55
    In other words it reverts to the ;) – David Jan 03 '16 at 04:58
  • No info about how to get commit_hash. -1 – user1 Jun 07 '16 at 15:15
  • @rednaw I know, I googled it. git rev-parse HEAD shows hash of the latest commit, so it's even better. Could you update your answer with that info? – user1 Jun 07 '16 at 15:35
  • 1
    @mr_jigsaw We have other SO questions for that ;) – gitaarik Jun 07 '16 at 16:00
  • 4
    In git documentation, it says that revert command reverts commits between first and last commits (both first and last included). [See Documentation](https://git-scm.com/docs/git-revert) – Onur Demir Aug 29 '16 at 12:55
  • 7
    @aod is correct, this answer needs to be updated. the current git API for revert has `` being included in the list of reverts – JHixson Jan 19 '18 at 18:24
  • 10
    with git 2.17.2 revert .. does not include old but includes – chingis Dec 18 '18 at 13:36
  • 2
    revert gives me `error: commit zzz is a merge but no -m option was given. fatal: revert failed` – t q Feb 15 '21 at 18:59
  • @weber A merge commit consists of 2 commits, the ones that are merged together. So if you want to revert a merge commit, you have to specify which of the 2 merged commits you want to revert. You do this with the `-m` option. Check `git revert --help` for more info. – gitaarik Feb 16 '21 at 21:30
  • I just tried `git revert 9d50f5bdffd2c0bb0088c936cfc73b0a66a49d40..4b0a43e1cd24ff41eb521b97c5cf9e10dc2544bc` on my code and it only reverted the last one of the 3 in this range! – Simon H Mar 11 '21 at 11:23
  • 18
    just tried....final step is missed, after **"git revert "** use **"git push origin "** – VicXj May 12 '21 at 08:53
  • 1
    The revert does not include the `oldest_commit_hash`, only the following commuts (up to and including `latest_commit_hash`). Could you update your answer? – Aron Fiechter Nov 24 '21 at 14:38
  • 1
    @AronFiechter Thanks, updated the answer to not including `` but added a note that on some versions of git it does include it, because some people seem to report that it does include it for them. I can't find anything about it in the git changelog about this, so not sure if it has to do with a git version or git config or something else. – gitaarik Nov 26 '21 at 14:37
  • I tried this. it, unfortunately, reverted the changes in my local machine and kept the remote unchanged – selmanioui Jul 05 '22 at 11:03
  • Hi @selmanioui after that, have you pushed that revert? – Quimbo Jul 05 '22 at 21:05
  • @Quimbo I haven't, because in my case I wanted to revert the pushed commit that's on the remote, if I do that, then all my work that's been pushed to the remote would've been gone. What I wanted was to only commit part of the changes that I made in the local to the remote, and the other part would only exist on my local. If I push the revert as you asked I would be losing the work from both my local and remote repos. – selmanioui Jul 06 '22 at 14:58
  • To discover the hash of your last commit use `git log -1` – MrWatson Oct 12 '22 at 06:02
  • This only reverted my last commit, then I found out I had to do `git revert --continue`. The problem is that the process looks never ending, is there a way to make it continue alone? I don't see any purpose, git already knows how the repository will look like after the process – Christian Vincenzo Traina Feb 07 '23 at 10:09
  • @ChristianVincenzoTraina I think it's best if you open a new question for this, describing your problem more verbosely, so people can help you better. – gitaarik Feb 07 '23 at 16:04
758

A solution that keeps no traces of the "undo".

NOTE: Don't do this if someone already pulled your change (I would use this only on my personal repo.)

Run:

git reset <previous label or sha1>

Note: previous means the commit before the erroneous commit

This will re-checkout all the updates locally (so git status will list all updated files, meaning, the files you changed/added/.. and were committed).

Then you "do your work" and re-commit your changes (Note: This step is optional).

git commit -am "blabla"

At this moment your local tree differs from the remote

git push -f <remote-name> <branch-name>

will force the remote branch to take this push and remove the previous one (specifying remote-name and branch-name is not mandatory but is recommended to avoid updating all branches with update flag).

!! Watch-out some tags may still be pointing removed commit !! how-to-delete-a-remote-tag

Anupam
  • 14,950
  • 19
  • 67
  • 94
jo_
  • 8,324
  • 1
  • 18
  • 14
  • thank but could u tell me why -am? what does it do? cant I do it without "-am"? – grep Oct 06 '15 at 16:44
  • 15
    -a all tracked files will be commited -m commit message follows – jo_ Oct 12 '15 at 15:52
  • 15
    Exactly what I searched for. Somebody did a faulty commit and push and reverted it again afterwards. The branches could not be merged because of this and I wanted to get the repository to be in the correct state again (and removing the commit from history as they were faulty ones anyway) – byemute Oct 30 '15 at 15:27
  • 6
    Which "previous label or sha1" should I use?. Should I enter the last "correct" one or the one before that and re-do all the changes done by the last correct one? – elysch Mar 28 '16 at 15:19
  • 24
    the one just before the eroneous commit – jo_ Mar 30 '16 at 17:07
  • Is the commit needed? Can't I just do a reset and force push it? – vrwim May 11 '16 at 13:52
  • yes you don't need to commit , just need to "push -f" to undo the previously pushed commit. (thanks vrwim I updated the post ) – jo_ May 18 '16 at 07:14
  • Doesn't seem to work for me, I ended up with a `fatal: You are not currently on a branch.` error when trying to `git push -f`. – zbr May 26 '16 at 08:33
  • @Zabri : is the commit you want to undo on the head of a branch ? (the answer must be yes to use this solution) – jo_ Jun 03 '16 at 09:10
  • 3
    Exactly what I need. I've pushed a branch to master by mistake. And as a result I had many rubbish commits during all commits history. I just done `git push -f` for last correct commit and remote history cleaned up! Thanks! – Anton Rybalko Dec 16 '16 at 13:04
  • can the author or community provide some valid scenarios where deleting the commit from the remote is "needed" instead of just "wanted"? sounds like 'byemute' has at least one valid scenario. but i would be interest to hear other scenarios. – Trevor Boyd Smith Oct 17 '17 at 12:24
  • @TrevorBoydSmith: This can have side effect this is why I started with a Note. But some times you are requested to have a clean repository thus removing an unwanted commit or re-organizing the commits is required. – jo_ Oct 19 '17 at 14:32
  • @jo_ you should add that the `git push -f` will force push other branches you have out of sync with origin. I had 2 other branches that were not synched with origin that got forced: ```develop -> develop (forced update) dusk/donation-tests -> dusk/donation-tests (forced update) hotfix/donation-update -> hotfix/donation-update (forced update) ``` – JonTroncoso Apr 18 '18 at 07:28
  • @JonTroncoso, sorry for the inconvenience; I updated the post. – jo_ Apr 19 '18 at 08:47
  • @jo_ its ok... was more about being informative then gripes about what i did. :) – JonTroncoso Apr 19 '18 at 11:03
  • You are the best the best – Nickool Mar 12 '19 at 00:55
  • `git reset HEAD^` will do for the first step if it's just the last commit you want to undo. Saves trying to figure out the right "previous label or sha1". – Heath Raftery Apr 21 '22 at 18:18
462

What I do in these cases is:

  • In the server, move the cursor back to the last known good commit:

    git push -f origin <last_known_good_commit>:<branch_name>
    
  • Locally, do the same:

    git reset --hard <last_known_good_commit>
    #         ^^^^^^
    #         optional
    



See a full example on a branch my_new_branch that I created for this purpose:

$ git branch
my_new_branch

This is the recent history after adding some stuff to myfile.py:

$ git log
commit 80143bcaaca77963a47c211a9cbe664d5448d546
Author: me
Date:   Wed Mar 23 12:48:03 2016 +0100

    Adding new stuff in myfile.py

commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit

I want to get rid of the last commit, which was already pushed, so I run:

$ git push -f origin b4zad078237fa48746a4feb6517fa409f6bf238e:my_new_branch
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:me/myrepo.git
 + 80143bc...b4zad07 b4zad078237fa48746a4feb6517fa409f6bf238e -> my_new_branch (forced update)

Nice! Now I see the file that was changed on that commit (myfile.py) shows in "not staged for commit":

$ git status
On branch my_new_branch
Your branch is up-to-date with 'origin/my_new_branch'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   myfile.py

no changes added to commit (use "git add" and/or "git commit -a")

Since I don't want these changes, I just move the cursor back locally as well:

$ git reset --hard b4zad078237fa48746a4feb6517fa409f6bf238e
HEAD is now at b4zad07 Initial commit

So now HEAD is in the previous commit, both in local and remote:

$ git log
commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • 3
    This is the correct answer! It is regarding pushed(!) commits! – powtac Jun 29 '18 at 14:07
  • 12
    If you want to truly revert changes (as if you've never pushed them), this _is_ the correct answer. – Jacob May 13 '19 at 06:27
  • This has worked for me, too (as if I've never pushed them :)) – Nurhak Kaya Aug 07 '19 at 17:01
  • what if my repo has only 1 commit pushed to server and i want to revert it? your answer is dependent on having a `last_known_good_commit` but i don't have any – ansh sachdeva Aug 26 '19 at 20:20
  • @anshsachdeva mmm good question. Wouldn't it be faster to just remove the repo and create it over again? – fedorqui Aug 28 '19 at 09:31
  • @fedorqui yes i know. was just asking if nuking the repo would be the correct way to handle this situation – ansh sachdeva Aug 28 '19 at 13:43
  • 1
    git push -f origin : for the devops/lead dev - constantly hotfixing - crazy people like me this is literally is the most awesome git command I have ever seen, thanks – alex Jan 19 '21 at 10:32
  • If you really don't know where the last know good commit was, you might want to take a look at git bisect. – openCivilisation May 08 '21 at 04:35
  • The change i did was indeed undone, but the commit (along with the commit message) still appears in the "commits" tab in Git. – Nisim Naim May 25 '21 at 10:49
  • ```git reset HEAD~``` Will reset the last commit locally without getting rid of your changes – Skillz Jul 01 '22 at 19:21
  • Beware of the last good commit hash you decide to work with. Say you are in your local user branch and the last good commit hash you chose is not from your branch then all the existing changes in your branch will be erased or over written by the files available in the branch's commit hash you chose. So make sure the last commit id you decide to reset is from your current working local branch. – Krishna Sundar Mar 17 '23 at 09:45
273

This will remove your pushed commits

git reset --hard 'xxxxx'

git clean -f -d

git push -f

Make a backup just in case

Two notes bumped up from comments:

  • 'xxxxx' is the hash code of the last commit that you want to keep.

  • clean is dangerous. It removes files, not under version control!

Michael Roswell
  • 1,300
  • 12
  • 31
Mo D Genesis
  • 5,187
  • 1
  • 21
  • 32
128

You can REVERT (or you can also call it DELETE) the Git Commit BOTH Locally and Remotely if you follow the steps as given below via git command line.

Run the following command to see the commit id that you want to revert

git log --oneline --decorate --graph

You will get like a following screenshot enter image description here

If you also check remote (via Web Interface) then you can see that this would be same as shown below

enter image description here

As per screenshot currently you are on commit id e110322 however you want to revert back to 030bbf6 BOTH LOCALLY and REMOTELY.

Perform the following steps to DELETE/REVERT Commits Locally+Remotely


First Locally Reverting to commit id 030bbf6

git reset --hard 030bbf6

followed by

git clean -f -d

These two commands clean force reset to commit stage 030bbf6 as shown below in snapshot

enter image description here

now if you run git status then you'll see that you are TWO Commits BEHIND from the remote branch as shown below enter image description here

Run following to update your indexes (if there are any updates). It is recommended that you ask all developers not to accept any pull requests on main remote branch.

git fetch --all

Once you are done with it then you are required to Push this commit forcefully by using + symbol in-front of branch as shown below. I have used here as master branch, you can replace it with any

enter image description here Code

git push -u origin +master

now if you see the web interface of remote then commit there should be reverted as well.

enter image description here

vibs2006
  • 6,028
  • 3
  • 40
  • 40
  • 1
    This is the best answer on here, because it actually explains with an example! Thanks for that. However, I get the following error: ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'gitlab.com:information-provision/collectors/deribit-collector.git' Despite having maintainer status etc – user6400946 Jul 19 '21 at 08:09
  • 1
    @user6400946 are you using '+' (plus) symbol as shown in the last command ? `git push -u origin +YourBrandName` – vibs2006 Jul 19 '21 at 12:04
  • 2
    Yes, howeverI just solved it. It seems my master branch was protected from force push and I needed to unprotect it temporarily (under gitlab repository settings). – user6400946 Jul 19 '21 at 12:56
  • This should be documented – Tuan Dat Tran Jun 16 '22 at 09:15
  • 1
    2023 and this is proper solution for me – L. Theodore Obonye Aug 24 '23 at 22:59
117

Let's say 61234 is the sha-number of the last good commit you want to keep.

git reset --hard 61234
git push -f


 will remove completely all wrong commits without any trace.

Note: If you wanted to push (the commit you reset to) to a specific branch you'd use git push -f origin branch-name instead.

Ryan Taylor
  • 12,559
  • 2
  • 39
  • 34
sepideha
  • 1,669
  • 1
  • 11
  • 14
  • 2
    Thank you! And the commit history is innocent! To convenient getting sha-number I've used: $ git log --oneline . – InaFK Apr 14 '21 at 11:16
  • I got `remote: GitLab: You are not allowed to force push code to a protected branch on this project.` error for running `git push -f origin master`. I think I have to change some configuration on my gitlab server. – Brian Oct 29 '21 at 09:15
  • I edited your answer because I almost ran `git push -f origin master` when I really wanted to reset a commit on a branch – glad it didn't go! – Ryan Taylor Dec 23 '21 at 17:27
  • 1
    I'm like you , always need push-back , use this way : git reset --hard and after git push -f origin branch-name – Esmaeil Ahmadipour Mar 08 '22 at 05:57
58

2020 Simple way :

git reset <commit_hash>

(The hash of the last commit you want to keep).

You will keep the now uncommitted changes locally.

If you want to push again, you have to do :

git push -f
Xys
  • 8,486
  • 2
  • 38
  • 56
  • 1
    It does not work always as the admin can block re-write history which is very common in the industry. So the solution mentioned by @gitaarik will work. git revert git push origin – tejp124 Oct 19 '20 at 13:26
34
git revert HEAD -m 1

In the above code line. "Last argument represents"

  • 1 - reverts one commits.

  • 2 - reverts last two commits.

  • n - reverts last n commits.

You need to push after this command to take the effect on remote. You have other options like specifying the range of commits to revert. This is one of the option.


Later use git commit -am "COMMIT_MESSAGE" then git push or git push -f

Sireesh Yarlagadda
  • 12,978
  • 3
  • 74
  • 76
  • 9
    This is not true - the -m parameter specifies number of parent to revert to (typically 1 if you want to revert the incoming, "theirs", changes, as opposed to 2 for merged-to changes, "ours" - for merging 2 commits). It has nothing to do with number of commits reverted - if you want to revert a commit range, use `git revert ref1..ref2` – Null Reference Oct 27 '16 at 13:18
  • 2
    Didn't have the desired effect – Sam Tuke Jun 04 '18 at 10:24
25

Here is my way:

Let's say the branch name is develop.

# Checkout a new temp branch based on one history commit
# (command to get history commit hashes: git log --oneline)
git checkout <last_known_good_commit_hash>

# Delete the original develop branch 
git branch -D develop
# Create a new develop branch based on the temp branch
git checkout -b develop

# Force update the remote with the new branch
git push -f origin develop

backslash112
  • 2,105
  • 1
  • 24
  • 29
  • I faced with illegal symbols on mac and only this solution helped me save the project. – storenth Jan 11 '23 at 17:55
  • It seems too risky to delete the develop branch just to undo a commit. What if it's a very busy project? – Nikita128 Jan 25 '23 at 17:35
  • @Nikita128 You are right, if it's a very busy project and this method will remove others commits for sure. – backslash112 Jan 25 '23 at 21:20
  • @Nikita128 It keeps old history intact. And you can keep a backup branch, just in case. But for me, this works like a charms. Other answers, keeps the deleted commits in history and I have to pull them again. – Dr. MAF May 15 '23 at 18:07
19

The reset hard worked for me: Thanks @Mo D Genensis and @vibs2006


git reset --hard 'your last working commit hash'

git clean -f -d

git push -f
villageek
  • 401
  • 4
  • 7
8

There are a number of ways like moving back commits by using git reset HEAD^, adding a ^ for every commit you want to go back, problem is the changes after that commit are then lost and can be added in a new commit. So I only do this when I only have to go one or two commits back.

To do it cleanly use the following:

git rebase -i <hash of last good commit, 9 chars is enough>

Now you will get a list of the commits from the last good commit to the HEAD with options what to do with each commit, going from the most recent at the top to the one just before the commit of the hash you added to the command. It will look something like this:

enter image description here

You can change the pick before every commit to any of the other options in the list of options. drop for instance, will remove the commit. You then save the file and the commits will be reapplied, performing the option you chose for every commit.

Now to fix the upstream do :

git push --force-with-lease

(With lease so you don't accidentally cause problems for someone else working on your pushed update)

This keeps the log clean by removing the wrong commit instead of introducing new commits fixing earlier erroneous commits.

[edit]: More clarification. And image example

AndrewRMillar
  • 608
  • 1
  • 7
  • 17
  • 1
    This doesn't work as it creates a diverged branch and you can't push it (not even with -force). But would be the nicest solution if it was working... – rimes Jun 11 '21 at 04:06
  • @rimes, I have used this type of construction, perhaps not often but, a number of times on code meant for production. A colleague (our resident git guru) uses it to squash code from PR's with a large number of commits to combine commits and keep the master branch log clean. – AndrewRMillar Jun 16 '21 at 08:47
7

git reset <commit_hash> (to get the<commit_hash> use git log --oneline)

git restore . to restore all the changed files to the version of yout target commit

git push origin master --force to force the push to your remote master branch. But be careful when using the force push if there is anyone working with you in the same branch

omaration
  • 126
  • 1
  • 6
3

You can do something like

git push origin +<short_commit_sha>^:<branch_name>
avrsanjay
  • 805
  • 7
  • 12
-1

Another way to do this without revert (traces of undo):

Don't do it if someone else has pushed other commits

Create a backup of your branch, being in your branch my-branch. So in case something goes wrong, you can restart the process without losing any work done.

git checkout -b my-branch-temp

Go back to your branch.

git checkout my-branch

Reset, to discard your last commit (to undo it):

git reset --hard HEAD^

Remove the branch on remote (ex. origin remote).

git push origin :my-branch

Repush your branch (without the unwanted commit) to the remote.

git push origin my-branch

Done!

I hope that helps! ;)

Gustavo Dias
  • 339
  • 2
  • 6