All of lore.kernel.org
 help / color / mirror / Atom feed
From: "John Cai via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "Phillip Wood" <phillip.wood123@gmail.com>,
	"Junio C Hamano" <gitster@pobox.com>,
	"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
	"Michael McClimon" <michael@mcclimon.org>,
	"John Cai" <johncai86@gmail.com>
Subject: [PATCH v5 0/2] rebase: update HEAD when is an oid
Date: Fri, 18 Mar 2022 13:54:01 +0000	[thread overview]
Message-ID: <pull.1226.v5.git.git.1647611643.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1226.v4.git.git.1647546828.gitgitgadget@gmail.com>

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/

This patch has two parts:

 * updates rebase setup test to add some tags we will use, while swapping
   out manual commit creation with the test_commit helper
 * sets RESET_HARD_DETACH flag if branch is not a valid refname

changes since v4:

 * got rid of test assertion that shows bug behavior
 * clarified commit message

changes since v3:

 * fixed typos in commit message
 * added a test assertion to show bug behavior
 * included more replacements with test_commit

changes since v2:

 * remove BUG assertion

changes since v1:

 * only set RESET_HEAD_DETACH if is not a valid branch
 * updated tests to use existing setup

John Cai (2):
  rebase: use test_commit helper in setup
  rebase: set REF_HEAD_DETACH in checkout_up_to_date()

 builtin/rebase.c  |  2 ++
 t/t3400-rebase.sh | 18 +++++++++++-------
 2 files changed, 13 insertions(+), 7 deletions(-)


base-commit: b896f729e240d250cf56899e6a0073f6aa469f5d
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1226%2Fjohn-cai%2Fjc%2Ffix-rebase-oids-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1226/john-cai/jc/fix-rebase-oids-v5
Pull-Request: https://github.com/git/git/pull/1226

Range-diff vs v4:

 1:  cac51a949ee < -:  ----------- rebase: test showing bug in rebase with non-branch
 2:  5c40e116eba ! 1:  9dbd2ba430a rebase: use test_commit helper in setup
     @@ t/t3400-rebase.sh: test_expect_success 'prepare repository with topic branches'
       	git checkout -f main &&
       	echo Third >>A &&
       	git update-index A &&
     - 	git commit -m "Modify A." &&
     - 	git checkout -b side my-topic-branch &&
     --	echo Side >>C &&
     --	git add C &&
     --	git commit -m "Add C" &&
     -+	test_commit --no-tag "Add C" C Side &&
     - 	git checkout -f my-topic-branch &&
     - 	git tag topic
     - '
     -@@ t/t3400-rebase.sh: test_expect_success 'rebase off of the previous branch using "-"' '
     - test_expect_success 'rebase a single mode change' '
     - 	git checkout main &&
     - 	git branch -D topic &&
     --	echo 1 >X &&
     --	git add X &&
     --	test_tick &&
     --	git commit -m prepare &&
     -+	test_commit prepare X 1 &&
     - 	git checkout -b modechange HEAD^ &&
     - 	echo 1 >X &&
     - 	git add X &&
 3:  13c5955c317 ! 2:  1dd5bb21012 rebase: set REF_HEAD_DETACH in checkout_up_to_date()
     @@ Metadata
       ## Commit message ##
          rebase: set REF_HEAD_DETACH in checkout_up_to_date()
      
     -    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). See [1] for
     -    the original bug report.
     +    "git rebase A B" where B is not a commit should behave as if the
     +    HEAD got detached at B and then the detached HEAD got rebased on top
     +    of A.  A bug however overwrites the current branch to point at B,
     +    when B is a descendant of A (i.e. the rebase ends up being a
     +    fast-forward).  See [1] for the original bug report.
      
          The callstack from checkout_up_to_date() is the following:
      
     -    cmd_rebase() -> checkout_up_to_date() -> reset_head() -> update_refs()
     -     -> update_ref()
     +    cmd_rebase()
     +    -> checkout_up_to_date()
     +       -> reset_head()
     +          -> update_refs()
     +             -> update_ref()
      
     -    When <branch> is not a valid branch but an oid, rebase sets the head_name
     -    of rebase_options to NULL. This value gets passed down this call chain
     -    through the branch member of reset_head_opts also getting set to NULL
     -    all the way to update_refs().
     +    When B is not a valid branch but an oid, rebase sets the head_name
     +    of rebase_options to NULL. This value gets passed down this call
     +    chain through the branch member of reset_head_opts also getting set
     +    to NULL all the way to update_refs().
      
          Then update_refs() checks ropts.branch to decide whether or not to switch
          branches. If ropts.branch is NULL, it calls update_ref() to update HEAD.
          At this point however, from rebase's point of view, we want a detached
          HEAD. But, since checkout_up_to_date() does not set the RESET_HEAD_DETACH
          flag, the update_ref() call will deference HEAD and update the branch its
     -    pointing to, which in the above example is main.
     -
     -    The correct behavior is that git rebase should update HEAD to $(git
     -    rev-parse topic) without dereferencing it.
     +    pointing to. We want the HEAD detached at B instead.
      
     -    Fix this bug by adding the RESET_HEAD_DETACH flag in checkout_up_to_date
     -    if <branch> is not a valid branch. 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.
     +    Fix this bug by adding the RESET_HEAD_DETACH flag in
     +    checkout_up_to_date if B is not a valid branch, 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 the correct behavior.
      
     -    1. https://lore.kernel.org/git/xmqqsfrpbepd.fsf@gitster.g/
     +    [1] https://lore.kernel.org/git/YiokTm3GxIZQQUow@newk/
      
     +    Reported-by: Michael McClimon <michael@mcclimon.org>
          Signed-off-by: John Cai <johncai86@gmail.com>
     +    Signed-off-by: Junio C Hamano <gitster@pobox.com>
      
       ## builtin/rebase.c ##
      @@ builtin/rebase.c: static int checkout_up_to_date(struct rebase_options *options)
     @@ t/t3400-rebase.sh: test_expect_success 'switch to branch not checked out' '
       	git rebase main other
       '
       
     --test_expect_success 'switch to non-branch changes branch HEAD points to' '
      +test_expect_success 'switch to non-branch detaches HEAD' '
     - 	git checkout main &&
     - 	old_main=$(git rev-parse HEAD) &&
     - 	git rebase First Second^0 &&
     --	test_cmp_rev HEAD main &&
     --	test_cmp_rev main $(git rev-parse Second) &&
     --	git symbolic-ref HEAD
     ++	git checkout main &&
     ++	old_main=$(git rev-parse HEAD) &&
     ++	git rebase First Second^0 &&
      +	test_cmp_rev HEAD Second &&
      +	test_cmp_rev main $old_main &&
      +	test_must_fail git symbolic-ref HEAD
     - '
     - 
     ++'
     ++
       test_expect_success 'refuse to switch to branch checked out elsewhere' '
     + 	git checkout main &&
     + 	git worktree add wt &&

-- 
gitgitgadget

  parent reply	other threads:[~2022-03-18 13:54 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-11  5:05 [PATCH] rebase: set REF_HEAD_DETACH in checkout_up_to_date() John Cai via GitGitGadget
2022-03-11  5:33 ` Junio C Hamano
2022-03-11  5:55 ` Junio C Hamano
2022-03-11 14:14   ` John Cai
2022-03-11 15:05 ` Phillip Wood
2022-03-11 15:28   ` John Cai
2022-03-11 19:42   ` John Cai
2022-03-11 21:31     ` Phillip Wood
2022-03-11 17:24 ` [PATCH v2 0/2] rebase: update HEAD when is an oid John Cai via GitGitGadget
2022-03-11 17:24   ` [PATCH v2 1/2] rebase: use test_commit helper in setup John Cai via GitGitGadget
2022-03-13  7:50     ` Junio C Hamano
2022-03-14 10:52       ` Phillip Wood
2022-03-14 21:47         ` Junio C Hamano
2022-03-11 17:24   ` [PATCH v2 2/2] rebase: set REF_HEAD_DETACH in checkout_up_to_date() John Cai via GitGitGadget
2022-03-13  7:58     ` Junio C Hamano
2022-03-14 10:54       ` Phillip Wood
2022-03-14 14:05         ` Phillip Wood
2022-03-14 14:11       ` John Cai
2022-03-14 14:25         ` Phillip Wood
2022-03-17  3:16   ` [PATCH v3 0/2] rebase: update HEAD when is an oid John Cai via GitGitGadget
2022-03-17  3:16     ` [PATCH v3 1/2] rebase: use test_commit helper in setup John Cai via GitGitGadget
2022-03-17 13:37       ` Ævar Arnfjörð Bjarmason
2022-03-17  3:16     ` [PATCH v3 2/2] rebase: set REF_HEAD_DETACH in checkout_up_to_date() John Cai via GitGitGadget
2022-03-17 13:42       ` Ævar Arnfjörð Bjarmason
2022-03-17 15:34         ` Junio C Hamano
2022-03-17 19:53     ` [PATCH v4 0/3] rebase: update HEAD when is an oid John Cai via GitGitGadget
2022-03-17 19:53       ` [PATCH v4 1/3] rebase: test showing bug in rebase with non-branch John Cai via GitGitGadget
2022-03-17 21:10         ` Junio C Hamano
2022-03-17 21:37           ` Junio C Hamano
2022-03-17 22:44           ` John Cai
2022-03-17 19:53       ` [PATCH v4 2/3] rebase: use test_commit helper in setup John Cai via GitGitGadget
2022-03-18 11:14         ` Phillip Wood
2022-03-18 13:06           ` John Cai
2022-03-17 19:53       ` [PATCH v4 3/3] rebase: set REF_HEAD_DETACH in checkout_up_to_date() John Cai via GitGitGadget
2022-03-17 21:36         ` Junio C Hamano
2022-03-18  0:30           ` John Cai
2022-03-18 13:54       ` John Cai via GitGitGadget [this message]
2022-03-18 13:54         ` [PATCH v5 1/2] rebase: use test_commit helper in setup John Cai via GitGitGadget
2022-03-18 16:51           ` Junio C Hamano
2022-03-18 13:54         ` [PATCH v5 2/2] rebase: set REF_HEAD_DETACH in checkout_up_to_date() John Cai via GitGitGadget
2022-03-18 16:55         ` [PATCH v5 0/2] rebase: update HEAD when is an oid Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.1226.v5.git.git.1647611643.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=avarab@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=johncai86@gmail.com \
    --cc=michael@mcclimon.org \
    --cc=phillip.wood123@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.