Starting with git 1.9/2.0 Q1 2014, you won't have to mark your previous branch origin before rebasing it on the rewritten upstream branch, as described in Aristotle Pagaltzis's answer:
See commit 07d406b and commit d96855f :
After working on the topic
branch created with git checkout -b topic origin/master
, the history of remote-tracking branch origin/master
may have been rewound and rebuilt, leading to a history 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 B3
, B2
, B1
and now it points at B
, and your topic
branch was started on top of it back when origin/master
was 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 option:
--fork-point::
Find the point at which a branch (or any history that leads to <commit>
) forked from another branch (or any reference) <ref>
.
This does not just look for the common ancestor 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 computes the fork point of the branch being rebased using the reflog entries of the "base
" branch (typically a remote-tracking branch) the branch's work was based on, in order to cope with the case in which the "base" branch has been rewound and rebuilt.
For example, if the history 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 through the output of "git rev-list --reflog base
" (i.e. B
, B1
, B2
, B3
) until it finds a commit that is an ancestor of the current tip "Derived (topic)
".
Internally, we have get_merge_bases_many()
that can compute this with one-go.
We would want a merge-base between Derived
and a fictitious merge commit that would result by merging all the historical tips of "base (origin/master)
".
When such a commit exist, we should get a single result, which exactly match one of the reflog entries of "base
".
Git 2.1 (Q3 2014) will add make this feature more robust to this: see commit 1e0dacd by John Keeping (johnkeeping
)
correctly handle the scenario where 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 correct result of git rebase master dev
is that B
is identified as the fork-point of dev
and master
, so that C
, D
, E
are the commits that need to be replayed onto master
; but C
and D
are patch-identical with C*
and D*
and so can be dropped, so that the end result is:
o --- B' --- C* --- D* --- E <- dev
If the fork-point is not identified, then picking B
onto a branch containing B'
results in a conflict and if the patch-identical commits are not correctly identified, then picking C
onto a branch containing D
(or equivalently D*
) results in a conflict.
The "--fork-point
" mode of "git rebase
" regressed when the command was rewritten in C back in 2.20 era, which has been corrected with Git 2.27 (Q2 2020).
See 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 [email protected]
"git rebase --fork-point master
" used to work OK, as it internally called "git merge-base --fork-point
" that knew how to handle short refname and dwim it to the full refname before calling the underlying get_fork_point()
function.
This is no longer true after the command was rewritten in C, as its internall call made directly to get_fork_point()
does not dwim a short ref.
Move the "dwim the refname argument to the full refname" logic that is used in "git merge-base" to the underlying get_fork_point()
function, so that the other caller of the function in the implementation of "git rebase" behaves the same way to fix this regression.
With Git 2.31 (Q1 2021), "git rebase --[no-]fork-point
"(man)" gained a configuration variable rebase.forkPoint
so that users do not have to keep specifying a non-default setting.
See commit 2803d80 (23 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 prefer to have this feature off by default because it can silently drop commits.
git config
now includes in its man page:
rebase.forkPoint
If set to false set --no-fork-point
option by default.
git pull --rebase && git push
. If you work onmaster
only, then this will very near unfailingly do the right thing for you, even if you’ve rebased and pushed at the other end. – Inappositegit reset --hard @{upstream}
now that I know that magic refspec incantation for "forget what I have/had, use what I fetched from the remote" See my final comment to https://mcmap.net/q/12353/-command-to-determine-the-upstream-ref-of-the-current-head – Seppalapush -f
): see my answer below – Stun