Git: How to ignore fast forward and revert origin [branch] to earlier commit?
Asked Answered
M

2

10

I used

git reset --hard dc082bc... 
to revert to the branch back to a required previous state, due to some bad commits. This has rewound my local branch fine. However, I want to rewind the branch on 'origin' to the same commit so that I can start again. Could anyone tell me how to revert the origin branch (not master) to this commit?

I've tried git push origin master, but it gives the following error

 ! [rejected]        branch -> branch (non-fast-forward)
error: failed to push some refs to '[email protected]:xxx/xxx.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.
Maricela answered 2/7, 2010 at 14:44 Comment(1)
You will soon (git1.8.5, Q4 2013) do a git push --force more carefully. See my new answer below.Duodenum
D
19

You can try git push --force to force the push.

--force

Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check.
This can cause the remote repository to lose commits; use it with care.

So if lots of people have already pulled the same branch from origin, that can cause some rebase issue on their side.
That operation can be blocked at the server side, like ebneter points out (in the comments):

Depending on how the remote is configured, though, this may not work
-- all of my central repos are configured with receive.denyNonFastForwards = true and receive.denyDeletes = true, in which case any such surgery has to be done on the remote server.

However, in the case of GitHub, such settings are not readily available for the user managing its GitHub repo.
So if you git push --force by mistake, all you are left is opening a case to the GitHub support, for them to check their local (i.e. "GitHub") reflogs and see if they can restore old commits.
(Since reflogs are local, like I have been remembered recently. So commits which are replaced by new ones during a push --force are only still visible, if no 'git gc' or 'git prune' already took place, at the GitHub server side)

So Marco Ceppi insists (in the comments):

this could really mess up other contributors local repos if you force pushes - though are times that it is just a necessary evil (I've maybe had to do this two times in my lifespan of using Git)

Duodenum answered 2/7, 2010 at 14:55 Comment(8)
git push -f does the same thing.Rego
@Marco: true. Although it really must be used carefully (see for instance support.github.com/discussions/repos/…)Duodenum
Depending on how the remote is configured, though, this may not work -- all of my central repos are configured with receive.denyNonFastForwards = true and receive.denyDeletes = true, in which case any such surgery has to be done on the remote server.Becalmed
@ebneter: but this is GitHub we are talking about here; it doesn't seem to offer that kind of server-side setting: support.github.com/discussions/feature-requests/…Duodenum
Ah, I didn't see that it was GitHub. Still, it's important to note in general.Becalmed
Correct - like VonC said - this could really mess up other contributors local repos if you force pushes - though are times that it is just a necessary evil (I've maybe had to do this two times in my lifespan of using Git).Rego
@ebneter: duly noted. Actually, I have updated my answer to include your comments and more.Duodenum
@VonC: Excellent. It's weird that GitHub doesn't let you configure things that way.Becalmed
D
33

To add to my previous answer, and to address the fact that a forced git push can really mess up other contributors' local repos, git 1.8.5 (upcoming Q4 2013) will see a new option:

git push --force-with-lease

See the origin of that option in this thread:

if something happens at 'origin' to the branch you are forcing or deleting since you fetched to inspect it, you may end up losing other people's work.

Somebody who is unaware of the decision to rewind and rebuild the branch may attempt to push to the branch between the time you fetched to rebase it and the time you pushed to replace it with the result of the rebasing.

We can make these pushes safer by optionally allowing the user to tell "git push" this:

I am forcing/deleting, based on the assumption that the value of 'branch' is still at this object.
If that assumption no longer holds, i.e. if something happened to the branch since I started preparing for this push, please do not proceed and fail this push.

You can see the full documentation of --force-with-lease in commit 28f5d17

--force-with-lease will protect all remote refs that are going to be updated by requiring their current value to be the same as some reasonable default, unless otherwise specified;

For now, "some reasonable default" is tentatively defined as "the value of the remote-tracking branch we have for the ref of the remote being updated", and it is an error if we do not have such a remote-tracking branch.

That explain the "lease" part of that option:

"force-with-lease": You assume you took the lease on the ref when you fetched to decide what the rebased history should be, and you can push back only if the lease has not been broken.


This is already being tested, and mentioned in the "What's cooking in git.git (Aug 2013, #07; Wed, 28)":

By the way, the push that overrides the usual "must fast-forward" was done using the "force-with-lease" option that has been cooking in next, like so:

$ git fetch ko next
$ anchor=$(git rev-parse --verify FETCH_HEAD)
$ for remote in ko repo gph github2
  do
    git push --force-with-lease=refs/heads/next:$anchor $remote next
  done

Note: "git push --force-with-lease" has been taught to report if the push needed to force (or fast-forwarded).

So this command is more detailed in its output with git 2.8 (March 2016)

push: fix ref status reporting for --force-with-lease

The --force--with-lease push option leads to less detailed status information than --force.
In particular, the output indicates that a reference was fast-forwarded, even when it was force-updated.


Beware of that option being ignored/bypassed, as explained in Git 2.13 (Q2 2017).

Duodenum answered 29/8, 2013 at 8:18 Comment(1)
And after a push --force, other can recover nicely: https://mcmap.net/q/12044/-how-do-i-recover-resynchronise-after-someone-pushes-a-rebase-or-a-reset-to-a-published-branchDuodenum
D
19

You can try git push --force to force the push.

--force

Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check.
This can cause the remote repository to lose commits; use it with care.

So if lots of people have already pulled the same branch from origin, that can cause some rebase issue on their side.
That operation can be blocked at the server side, like ebneter points out (in the comments):

Depending on how the remote is configured, though, this may not work
-- all of my central repos are configured with receive.denyNonFastForwards = true and receive.denyDeletes = true, in which case any such surgery has to be done on the remote server.

However, in the case of GitHub, such settings are not readily available for the user managing its GitHub repo.
So if you git push --force by mistake, all you are left is opening a case to the GitHub support, for them to check their local (i.e. "GitHub") reflogs and see if they can restore old commits.
(Since reflogs are local, like I have been remembered recently. So commits which are replaced by new ones during a push --force are only still visible, if no 'git gc' or 'git prune' already took place, at the GitHub server side)

So Marco Ceppi insists (in the comments):

this could really mess up other contributors local repos if you force pushes - though are times that it is just a necessary evil (I've maybe had to do this two times in my lifespan of using Git)

Duodenum answered 2/7, 2010 at 14:55 Comment(8)
git push -f does the same thing.Rego
@Marco: true. Although it really must be used carefully (see for instance support.github.com/discussions/repos/…)Duodenum
Depending on how the remote is configured, though, this may not work -- all of my central repos are configured with receive.denyNonFastForwards = true and receive.denyDeletes = true, in which case any such surgery has to be done on the remote server.Becalmed
@ebneter: but this is GitHub we are talking about here; it doesn't seem to offer that kind of server-side setting: support.github.com/discussions/feature-requests/…Duodenum
Ah, I didn't see that it was GitHub. Still, it's important to note in general.Becalmed
Correct - like VonC said - this could really mess up other contributors local repos if you force pushes - though are times that it is just a necessary evil (I've maybe had to do this two times in my lifespan of using Git).Rego
@ebneter: duly noted. Actually, I have updated my answer to include your comments and more.Duodenum
@VonC: Excellent. It's weird that GitHub doesn't let you configure things that way.Becalmed

© 2022 - 2024 — McMap. All rights reserved.