Git rebase - commit select in fork-point mode

Solution 1:

You are correct that $fork_point will be B3.

I believe the intent here is to omit B3 as "not your commit".

I think the diagram the Git folks drew here is not so good. Here's how I would redraw and rewrite it without changing it too much (though I'd probably just re-letter each commit anyway).


You begin by cloning (or otherwise updating to) some (origin) repository whose graph ends in commit B3, and you create a topic branch and make some commit(s):

...--o---F---B3    <-- origin/master
              \
               G   <-- topic

Over time, with additional git fetch-es and git commits, your commit graph now looks like this:

...--o---F---B3--B2--B1    <-- origin/master
              \
               G---H---I   <-- topic

But, suddenly, after another git fetch, your own commit graph now looks like this:

                 o---B1'       <-- origin/foo
                /
...o---F---B2'-o---o---o---B   <-- origin/master
        \
         B3--G---H---I         <-- topic

That is, Git would now think that commit B3 belongs on your topic branch, when in fact, your work begins with commit G. The people who own the repository named origin have, in effect, declared that commit B3 is terrible and should be thrown away. (They kept a copy of B2 as B2' on their master, and one of B1 as B1' on their foo.)

If you simply git rebase, you will copy original commit B3 to new copy B3' (while also copying G-H-I):

                 o---B1'                     <-- origin/foo
                /
...o---F---B2'-o---o---o---B                 <-- origin/master
                            \
                             B3'-G'--H'--I'  <-- topic

but you would instead prefer:

                 o---B1'                 <-- origin/foo
                /
...o---F---B2'-o---o---o---B             <-- origin/master
                            \
                             G'--H'--I   <-- topic

For git rebase to do this, you must instruct Git to locate commit B3. Your reflog for origin/master has all of F, B3, B2, and B1 in it (under at least one reflog entry, including in this case origin/master@{1}), while your own topic has F and B3, but not B2 nor B1, in it as well. Therefore --fork-point chooses B3 as the newest (tip-most) shared commit, rather than F.


The key sentence / idea in here is that the upstream repository writers intended to discard commit B3 entirely.

(How you are supposed to know this for certain is a bit of a mystery. It may not be obvious that B2' and B1' are copies, if the rebasing required, e.g., discarding a file that should never have been committed—and was in B1, which is why B1 was also discarded. The fact that this file is now omitted in B2' and B3' makes them not-patch-equivalent, hence not obviously copies.)

(Note that your own master also still points to B3!)