[ACCEPTED]-How do I recover/resynchronise after someone pushes a rebase or a reset to a published branch?-git-reset

Accepted answer
Score: 75

Getting back in synch after a pushed rebase 16 is really not that complicated in most cases.

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

Ie. first 15 you set up a bookmark for where the remote 14 branch originally was, then you use that 13 to replay your local commits from that point 12 onward onto rebased remote branch.

Rebasing 11 is like violence: if it doesn’t solve your 10 problem, you just need more of it. ☺

You 9 can do this without the bookmark of course, if 8 you look up the pre-rebase origin/foo commit ID, and 7 use that.

This is also how you deal with 6 the situation where you forgot to make a 5 bookmark before fetching. Nothing is lost – you 4 just need to check the reflog for the remote 3 branch:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

This will print the commit ID that 2 origin/foo pointed to before the most recent fetch 1 that changed its history.

You can then simply

git rebase --onto origin/foo $commit foo
Score: 11

I'd say the recovering from upstream rebase section of the git-rebase man 5 page covers pretty much all of this.

It's 4 really no different from recovering from 3 your own rebase - you move one branch, and 2 rebase all branches which had it in their 1 history onto its new position.

Score: 11

Starting with git 1.9/2.0 Q1 2014, you won't 72 have to mark your previous branch origin 71 before rebasing it on the rewritten upstream 70 branch, as described in Aristotle Pagaltzis's answer:
See commit 07d406b and commit d96855f :

After 69 working on the topic branch created with git checkout -b topic origin/master, the 68 history of remote-tracking branch origin/master may have 67 been rewound and rebuilt, leading to a history 66 of this shape:

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

where origin/master used to point at commits 65 B3, B2, B1 and now it points at B, and your topic branch 64 was started on top of it back when origin/master was 63 at B3.

This mode uses the reflog of origin/master to find B3 as the fork point, so that the topic can be rebased on top of the updated origin/master by:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

That is why the git merge-base command has a new 62 option:

--fork-point::

Find the point at which a branch 61 (or any history that leads to <commit>) forked 60 from another branch (or any reference) <ref>.
This 59 does not just look for the common ancestor 58 of the two commits, but also takes into account the reflog of <ref> to see if the history leading to <commit> forked from an earlier incarnation of the branch <ref>.


The "git pull --rebase" command 57 computes the fork point of the branch being 56 rebased using the reflog entries of the 55 "base" branch (typically a remote-tracking 54 branch) the branch's work was based on, in 53 order to cope with the case in which the 52 "base" branch has been rewound 51 and rebuilt.

For example, if the history 50 looked like where:

  • the current tip of the "base" branch is at B, but earlier fetch observed that its tip used to be B3 and then B2 and then B1 before getting to the current commit, and
  • the branch being rebased on top of the latest "base" is based on commit B3,

it tries to find B3 by going 49 through the output of "git rev-list --reflog base" (i.e. B, B1, B2, B3) until 48 it finds a commit that is an ancestor of 47 the current tip "Derived (topic)".

Internally, we 46 have get_merge_bases_many() that can compute this with one-go.
We 45 would want a merge-base between Derived and a fictitious 44 merge commit that would result by merging 43 all the historical tips of "base (origin/master)".
When 42 such a commit exist, we should get a single 41 result, which exactly match one of the reflog 40 entries of "base".


Git 2.1 (Q3 2014) will 39 add make this feature more robust to this: see 38 commit 1e0dacd by John Keeping (johnkeeping)

correctly handle the scenario where 37 we have the following topology:

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

where:

  • B' is a fixed-up version of B that is not patch-identical with B;
  • C* and D* are patch-identical to C and D respectively and conflict textually if applied in the wrong order;
  • E depends textually on D.

The 36 correct result of git rebase master dev is that B is identified 35 as the fork-point of dev and master, so that C, D, E are 34 the commits that need to be replayed onto 33 master; but C and D are patch-identical with C* and 32 D* and so can be dropped, so that the end 31 result is:

o --- B' --- C* --- D* --- E  <- dev

If the fork-point is not identified, then 30 picking B onto a branch containing B' results 29 in a conflict and if the patch-identical 28 commits are not correctly identified then 27 picking C onto a branch containing D (or equivalently 26 D*) results in a conflict.


The "--fork-point" mode 25 of "git rebase" regressed when the command 24 was rewritten in C back in 2.20 era, which 23 has been corrected with Git 2.27 (Q2 2020).

See 22 commit f08132f (09 Dec 2019) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit fb4175b, 27 Mar 2020)

rebase: --fork-point regression fix

Signed-off-by: Alex Torok
[jc: revamped the fix and used Alex's tests]
Signed-off-by: Junio C Hamano gitster@pobox.com

"git rebase --fork-point master" used to 21 work OK, as it internally called "git merge-base --fork-point" that 20 knew how to handle short refname and dwim 19 it to the full refname before calling the 18 underlying get_fork_point() function.

This is no longer true 17 after the command was rewritten in C, as 16 its internall call made directly to get_fork_point() does 15 not dwim a short ref.

Move the "dwim 14 the refname argument to the full refname" logic 13 that is used in "git merge-base" to 12 the underlying get_fork_point() function, so that the other 11 caller of the function in the implementation 10 of "git rebase" behaves the same 9 way to fix this regression.


With Git 2.31 8 (Q1 2021), "git rebase --[no-]fork-point"(man)" gained a configuration 7 variable rebase.forkPoint so that users do not have to keep 6 specifying a non-default setting.

See commit 2803d80 (23 5 Feb 2021) by Alex Henrie (alexhenrie).
(Merged by Junio C Hamano -- gitster -- in commit 682bbad, 25 Feb 2021)

rebase: add a config option for --no-fork-point

Signed-off-by: Alex Henrie

Some users (myself included) would 4 prefer to have this feature off by default 3 because it can silently drop commits.

git config now 2 includes in its man page:

rebase.forkPoint

If set to false set --no-fork-point option 1 by default.

More Related questions