From: John Cai <johncai86@xxxxxxxxx> Fixes a bug whereby rebase updates the deferenced reference HEAD points to instead of HEAD directly. If HEAD is on main and if the following is a fast-forward operation, git rebase $(git rev-parse main) $(git rev-parse topic) Instead of HEAD being set to $(git rev-parse topic), rebase erroneously dereferences HEAD and sets main to $(git rev-parse topic). This bug was reported by Michael McClimon. See [1]. This is happening because on a fast foward with an oid as a <branch>, update_refs() will only call update_ref() with REF_NO_DEREF if RESET_HEAD_DETACH is set. This change was made in 176f5d96 (built-in rebase --autostash: leave the current branch alone if possible, 2018-11-07). In rebase, we are not setting the RESET_HEAD_DETACH flag, which means that the update_ref() call ends up dereferencing HEAD and updating it to the oid used as <branch>. The correct behavior is that git rebase should update HEAD to $(git rev-parse topic) without dereferencing it. Fix this bug by adding the RESET_HEAD_DETACH flag in checkout_up_to_date so that once reset_head() calls update_refs(), it calls update_ref() with REF_NO_DEREF which updates HEAD directly intead of deferencing it and updating the branch that HEAD points to. Also add a test to ensure this behavior. 1. https://lore.kernel.org/git/xmqqsfrpbepd.fsf@gitster.g/ Signed-off-by: John Cai <johncai86@xxxxxxxxx> --- rebase: update HEAD when is an oid Fixes a bug [1] reported by Michael McClimon where rebase with oids will erroneously update the branch HEAD points to. 1. https://lore.kernel.org/git/xmqqsfrpbepd.fsf@gitster.g/ Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1226%2Fjohn-cai%2Fjc%2Ffix-rebase-oids-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1226/john-cai/jc/fix-rebase-oids-v1 Pull-Request: https://github.com/git/git/pull/1226 builtin/rebase.c | 2 +- t/t3400-rebase.sh | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index b29ad2b65e7..52afeffcc2e 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -828,7 +828,7 @@ static int checkout_up_to_date(struct rebase_options *options) options->switch_to); ropts.oid = &options->orig_head; ropts.branch = options->head_name; - ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK | RESET_HEAD_DETACH; ropts.head_msg = buf.buf; if (reset_head(the_repository, &ropts) < 0) ret = error(_("could not switch to %s"), options->switch_to); diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 71b1735e1dd..0b92e78976c 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -437,4 +437,25 @@ test_expect_success 'rebase when inside worktree subdirectory' ' ) ' +test_expect_success 'rebase with oids' ' + git init main-wt && + ( + cd main-wt && + >file && + git add file && + git commit -m initial && + git checkout -b side && + echo >>file && + git commit -a -m side && + git checkout main && + git tag hold && + git checkout -B main hold && + git rev-parse main >pre && + git rebase $(git rev-parse main) $(git rev-parse side) && + git rev-parse main >post && + test "$(git rev-parse side)" = "$(cat .git/HEAD)" && + test_cmp pre post + ) +' + test_done base-commit: 1a4874565fa3b6668042216189551b98b4dc0b1b -- gitgitgadget