If I use push -f
to overlap the last commit, namely A, on remote repository, and others have pulled A before I pushed.
Anyway to undo this in case not to cause trouble for others?
Is it useful to push -f
an original one to "pretend" that nothing has been touched?
Or
How git decides whether the local repo diverges from the remote tracking one ?
There are a few ways to find out the original HEAD before the push:
If you're lucky enough to have the terminal open still, there will be some output when the push was made that looks like this:
...
To user@host:repo.git
+ abcdef0...1234567 HEAD -> branchname (forced update)
Here, abcdef0
was the previous HEAD (your A
) and 1234567
was what you forced it to be instead.
git reflog
The output of git reflog
tells you the chronological history of what you did. You basically want to go back to the line in question (where you had checked out your branch before the changes) and grab the commit ID from the first column.
The most helpful command here is git reflog show remotes/origin/branchname
. This should show you your forced update (1234567
) and the previous commit ID (abcdef0
) as the top two lines.
A couple of commit references might be useful here:
@{1}
(or branchname@{1}
, if you are not on that branch) is the prior value of that reference. Only works if you haven't done any other commits to your local branch. (But @{2}
, @{3}
etc will allow you to go further back.)remotes/origin/branchname@{1}
will be the prior value of the ref on the remote. Only works if someone else hasn't pushed to the remote. (Same point about @{n}
above.)If you want to confirm that the ID you've grabbed from one of the above methods is correct, simply check it out:
git checkout abcdef0
and have a look around. If the git log
looks familiar (I also recommend tig for browsing your repository, and you can even run tig abcdef0
to look at the log from a given commit), then you can be confident that you're resetting to the right place.
Once you have the previous commit ID, you can reset to that and force push again:
git checkout branchname # if you're not on it already
git reset --hard abcdef0
git push -f
or just:
git push -f origin abcdef0:branchname
This will restore the state of the branch to the state before the forced push. (Quick note: that first snippet will update your local branch as well as the remote; the second one only updates the remote.)
If people have pulled the branch since you force pushed, if you were to force push back to the original then they will run into problems when subsequently updating. If they've not made any commits to that branch, they can either just delete their local branch and re-checkout (after a git fetch
to make sure they have up-to-date references), or the following will do the same:
git fetch
git checkout branchname # if you're not on it already
git reset --hard origin/branchname
If they have made local commits, then they will need to rebase those changes onto the correct history (and possibly resolve any conflicts):
git fetch
git checkout branchname # if you're not on it already
git rebase --onto origin/branchname 1234567
The above means "replay all commits after 1234567
(the incorrect head) on top of origin/branchname
(the correct head)."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With