1117

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
  • 25,247
  • 27
  • 110
  • 174
Manolo
  • 20,308
  • 18
  • 73
  • 124

14 Answers14

1316

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 know 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
  • 36,986
  • 11
  • 91
  • 97
  • 46
    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
  • @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
  • 1
    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
  • 12
    just tried....final step is missed, after **"git revert "** use **"git push origin "** – VicXj May 12 '21 at 08:53
  • Thank you for this! – Hrvoje Čukman Nov 02 '21 at 11:17
  • 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
579

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>

this will re-checkout all the updates locally (so git status will list all updated files)

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

aerin
  • 16,939
  • 27
  • 90
  • 123
jo_
  • 6,448
  • 1
  • 15
  • 12
  • 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
  • 10
    -a all tracked files will be commited -m commit message follows – jo_ Oct 12 '15 at 15:52
  • 8
    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
  • 3
    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
  • 12
    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
  • 1
    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
368

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
  • 252,262
  • 96
  • 511
  • 570
183

This will remove your pushed commits

git reset --hard 'xxxxx'

git clean -f -d

git push -f
Mo D Genesis
  • 3,455
  • 1
  • 16
  • 27
103

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
  • 5,429
  • 3
  • 37
  • 37
  • 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
  • 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
  • 1
    works well in 2022 :D – danu Mar 02 '22 at 01:44
93

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
  • 10,951
  • 2
  • 36
  • 33
sepideha
  • 1,389
  • 8
  • 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
  • 2
    This should have ben the accepted answer! – Stone Sep 17 '21 at 18:22
  • 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
47

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
  • 5,826
  • 2
  • 31
  • 47
  • 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
  • This is short, simple, and it does exactly what I need. Thanks! – RedGuy11 Jul 08 '21 at 15:55
26
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
  • 11,766
  • 3
  • 71
  • 74
  • 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
  • 1
    Didn't have the desired effect – Sam Tuke Jun 04 '18 at 10:24
17

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
  • 351
  • 3
  • 7
16

Here is my way:

Let's say the branch name is develop.

# Checkout a new temp branch based on one history commit(cmd to get history: git log)
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 this new branch
git push -f origin develop

backslash112
  • 1,897
  • 1
  • 22
  • 29
3

To do it cleanly:

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. DROP will throw away that commit. Save the file.

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.

Sl4rtib4rtf4st
  • 468
  • 5
  • 15
  • 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. – Sl4rtib4rtf4st Jun 16 '21 at 08:47
2

You can do something like

git push origin +<short_commit_sha>^:<branch_name>
avrsanjay
  • 785
  • 6
  • 12
0

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
  • 36
  • 5
-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
  • 159
  • 6