David van Dugteren
David van Dugteren

Reputation: 4109

How can I recover from an erronous git push -f origin master?

I just committed the wrong source to my project using --force option.

Is it possible to revert? I understand that all previous branches have been overwritten using -f option, so I may have screwed up my previous revisions.

Upvotes: 145

Views: 151397

Answers (11)

brandizzi
brandizzi

Reputation: 27050

Here is a way to restore a wrongly force-pushed branch on Github without having to fiddle with API requests.

Consider this scenario:

  • you ran git push --force SOME_BRANCH
  • the remote SOME_BRANCH had some work on it you did not include in your local branch;
  • worse yet, you didn't have an up-to-date version of SOME_BRANCH in your machine, so you do not even have the chance of looking at your reflogs.

Here is a solution:

  1. go to the repository, and click on the "Activity' option: github screenshot with "Activity" link on the right column

  2. Look at the list of activities and find the forced-push event. There, click in the dot menu > Compare changes. github screenshot showing list of activities

  3. There, the original commit will appear as the base one. You can copy the hash of this commit from there. It is also on the address bar, where it is easier to fetch. Github screenshot showing comparison between original and force-pushed commit

  4. Fetch this commit locally:

    git fetch origin  19399d67[...]a8
    

    Note that you need the entire hash (in our case 19399d67[...]a8). You cannot use one of the abbreviated versions (such as 19399d67)

  5. Change your branch to use the right version.

    git co -B SOME_BRANCH  19399d67[...]a8
    
  6. Now force-push it again. I know, it is scary 😖

Voilà. The older version of the branch is used now both locally and remotely. You can work on it now.

Upvotes: 2

Abdelhafid
Abdelhafid

Reputation: 919

The solution is already mentioned here

# work on local main
git checkout main

# reset to the previous state of origin/main, as recorded by reflog
git reset --hard origin/main@{1}

# at this point verify that this is indeed the desired commit.
# (if necessary, use git reflog to find the right one, and
# git reset --hard to that one)

# finally, push the mainbranch (and only the main branch) to the server
git push -f origin main

Upvotes: 68

Vignesh Raja
Vignesh Raja

Reputation: 8751

I tried as below. Hope someone might benefit.

5794458...b459f069 dev_branch -> dev_branch (forced update)

Go to GitLab -> Repository menu -> Branches option -> New Branch

Enter Branch Name, Create From as first COMMIT_ID from above message and Enter -> Create Branch.

This method solved my case and restored all the changes into new branch.

Upvotes: 0

Anthony Krivonos
Anthony Krivonos

Reputation: 4726

For people in really bad situations like I was (for example, if you're getting bad object errors when running git reset --hard):

I wrote a script called treesaver that pulls all your files from the GitHub API as a last resort. Here's how to use it:

  1. Clone the treesaver script and cd to it.
  2. Find the SHA string of the tree you'd like to restore by accessing https://api.github.com/repos/<your_username_or_org>/<repo>/events.
  3. In the payload property corresponding to your push event, find the commit you'd like to revert to and click on its url.
  4. Under commit.tree, copy the tree's url.
  5. Run python3 main.py <tree_url> <path_to_save_to>.

For example, in my case, I would run:

python3 main.py https://api.github.com/repos/anthonykrivonos/my-repo/git/trees/1234567 .

Of course, PRs welcome.

Upvotes: 2

يعقوب
يعقوب

Reputation: 1222

Yes you can recover commits after git push -f your_branch

Text from Doc:

Prune entries older than the specified time. If this option is not specified, the expiration time is taken from the configuration setting gc.reflogExpire, which in turn defaults to 90 days. --expire=all prunes entries regardless of their age; --expire=never turns off pruning of reachable entries (but see --expire-unreachable).

So you can do:

1- git reflog

enter image description here

2- you choose Head_Number does you want recover with git reset –hard HEAD@{HEAD-NUMBER}

enter image description here

3- you can see all commits on this head by git cherry -v branch_name

4- in the end you should force push git push -f branch_name

OR

1- get the number of SHA from your GIT client (interface)

git reset --hard commit_SHA

2- force push

git push -f your_branch

Hope this helps

Upvotes: 46

Andrey
Andrey

Reputation: 371

Here you can read decisions https://evilmartians.com/chronicles/git-push---force-and-how-to-deal-with-it

The second one helped me. I did wrong these commands

1) (some-branch) git pull -> correct command was git pull origin some-branch

2) (some-branch) git push -f origin some-branch

After these commands I lost three commits. To recover them I looked to terminal where I did wrongly 'git pull' and have seen there output like

60223bf...0b258eb some-branch -> origin/some-branch

The second hash 0b258eb was exactly what I needed. So, I took this hash and produce command

git push --force origin 0b258eb:some-branch

Upvotes: 0

user7610
user7610

Reputation: 28811

Another way to recover the lost commit or even to figure out what commits were lost, if the previous push came not from your local repo, is to look at your CI machine.

If you have a job which tests the master branch after every commit (or series of consecutive commits), which you should have, you can have a look what it was testing last. That is the commit you need to restore.

The CI machine may even keep a local clone of the repo, from which you may be able to perform this recovery.

Source: probably Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation (Addison-Wesley Signature Series (Fowler))

Upvotes: 5

Pierrick HYMBERT
Pierrick HYMBERT

Reputation: 797

If you are not on that local repo where the forced push came from, at origin/master level there is no way to recover. But if you are lucky enough to use GitHub or GitHub for Enterprise, you can have a look to the REST API and retrieve lost commit as patch, example:

  1. List events and find the commit sha1 long format

https://api.github.com/repos/apache/logging-log4j2/events

  1. Download the lost commit and retrieve the related patch in the json path .files[]/patch

https://api.github.com/repos/apache/logging-log4j2/commits/889232e28f3863d2a17392c06c1dd8cac68485de

  1. Apply locally and push again

git apply patch.patch && git commit -m "restored commit" && git push origin master

Upvotes: 12

user1094125
user1094125

Reputation: 769

If you know the commit hash, it's easy, just recreate your branch.

5794458...b459f069 master -> master (forced update)

Delete the remote branch:

git push origin :master

then recreate your branch with the following commands:

git checkout 5794458
git branch master
git push origin master

Upvotes: 71

Cameron Skinner
Cameron Skinner

Reputation: 54306

Git generally doesn't throw anything away, but recovering from this may still be tricky.

If you have the correct source then you could just push it into the remote with the --force option. Git won't have deleted any branches unless you told it to. If you have actually lost commits then take a look at this useful guide to recovering commits. If you know the SHA-1 of the commits you want then you're probably OK.

Best thing to do: Back everything up and see what is still in your local repository. Do the same on the remote if possible. Use git fsck to see if you can recover things, and above all DO NOT run git gc.

Above above all, never use the --force option unless you really, really mean it.

Upvotes: 77

Pran
Pran

Reputation: 21

I did the same thing while undoing a last push for only one file. Ended up going to back to original state of the repository. I was using git commands from Linus as I had the local copy on Linux. Luckily that copy was still intact.

All I did was (after frantically making few more copies of the local repo):

git add .
git status

(it said that origin/master was ahead by 68 commits, fine ... those were all the commits I deleted)

git remote set-url origin <GIT_SSH_URL>
git push

And everything got restored the way it was before I did forceful push. The most important thing to remember is never to do a git checkout . after you had forcefully pushed. But the best practice is to disable push option. I am never using it ever again. Learnt my lesson!!

Upvotes: 2

Related Questions