git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
@ 2024-09-01 11:02 Han Jiang
  2024-09-01 15:26 ` Phillip Wood
  0 siblings, 1 reply; 6+ messages in thread
From: Han Jiang @ 2024-09-01 11:02 UTC (permalink / raw)
  To: git

Thank you for filling out a Git bug report!
Please answer the following questions to help us understand your issue.

What did you do before the bug happened? (Steps to reproduce your issue)

cd '/'; cd '/'; rm --force --recursive -- './test_git'; mkdir "$_"; cd "$_";
mkdir --parents -- './server' './client';
git -C './server' init --bare './repo.git'
echo '1' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
echo '2' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
git -C './server/repo.git' log --graph --all --patch

git -C './client' clone '../server/repo.git' './repo'
branch_default_path="$(git -C './client/repo' symbolic-ref HEAD)"
branch_default_name="${branch_default_path#'refs/heads/'}"
echo '3' >'./client/repo/file'; git -C './client/repo' add './file';
git -C './client/repo' commit -m "$((++number))"
git -C './client/repo' log --graph --all --patch

git -C './server/repo.git' reset --soft HEAD~1
echo '4' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
git -C './server/repo.git' log --graph --all --patch

git -C './client/repo' fetch --all
git -C './client/repo' log --graph --all --patch
git -C './client/repo' -c 'core.editor=cat' rebase
--onto=HEAD@{upstream} --interactive "$(git -C './client/repo'
merge-base --fork-point HEAD@{upstream} HEAD)" HEAD
git -C './client/repo' log --graph --all --patch
echo '5' >'./client/repo/file'; git -C './client/repo' add './file';
git -C './client/repo' -c 'core.editor=:' rebase --continue
git -C './client/repo' log --graph --all --patch
git -C './client/repo' checkout -B "$branch_default_name"
git -C './client/repo' log --graph --all --patch

What did you expect to happen? (Expected behavior)

HEAD points to GIT_DEFAULT_BRANCH and GIT_DEFAULT_BRANCH points to tip
of origin's GIT_DEFAULT_BRANCH when rebase completes.

What happened instead? (Actual behavior)

HEAD is detached and *HEAD is not moved when rebase completes, so `git
checkout -B` is needed.

What's different between what you expected and what actually happened?

Anything else you want to add:

Replacing `HEAD` with branch name (`"$(git -C './client/repo' branch
--show-current)"`) works around the problem.
cd '/'; cd '/'; rm --force --recursive -- './test_git'; mkdir "$_"; cd "$_";
mkdir --parents -- './server' './client';
git -C './server' init --bare './repo.git'
echo '1' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
echo '2' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
git -C './server/repo.git' log --graph --all --patch

git -C './client' clone '../server/repo.git' './repo'
echo '3' >'./client/repo/file'; git -C './client/repo' add './file';
git -C './client/repo' commit -m "$((++number))"
git -C './client/repo' log --graph --all --patch

git -C './server/repo.git' reset --soft HEAD~1
echo '4' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'
add './file'; git --git-dir='./server/repo.git' --work-tree='.' commit
-m "$((++number))"
git -C './server/repo.git' log --graph --all --patch

git -C './client/repo' fetch --all
git -C './client/repo' log --graph --all --patch
git -C './client/repo' -c 'core.editor=cat' rebase
--onto=HEAD@{upstream} --interactive "$(git -C './client/repo'
merge-base --fork-point HEAD@{upstream} HEAD)" "$(git -C
'./client/repo' branch --show-current)"
git -C './client/repo' log --graph --all --patch
echo '5' >'./client/repo/file'; git -C './client/repo' add './file';
git -C './client/repo' -c 'core.editor=:' rebase --continue
git -C './client/repo' log --graph --all --patch

Please review the rest of the bug report below.
You can delete any lines you don't wish to share.


[System Info]
git version:
git version 2.46.0.windows.1
cpu: x86_64
built from commit: 2e6a859ffc0471f60f79c1256f766042b0d5d17d
sizeof-long: 4
sizeof-size_t: 8
shell-path: D:/git-sdk-64-build-installers/usr/bin/sh
feature: fsmonitor--daemon
libcurl: 8.9.0
OpenSSL: OpenSSL 3.2.2 4 Jun 2024
zlib: 1.3.1
uname: Windows 10.0 22631
compiler info: gnuc: 14.1
libc info: no libc information available
$SHELL (typically, interactive shell): C:\Program Files\Git\usr\bin\bash.exe


[Enabled Hooks]
not run from a git repository - no hooks to show

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
  2024-09-01 11:02 `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD` Han Jiang
@ 2024-09-01 15:26 ` Phillip Wood
  2024-09-01 15:42   ` Junio C Hamano
  0 siblings, 1 reply; 6+ messages in thread
From: Phillip Wood @ 2024-09-01 15:26 UTC (permalink / raw)
  To: Han Jiang, git

On 01/09/2024 12:02, Han Jiang wrote:
> What did you do before the bug happened? (Steps to reproduce your issue)

> git -C './client/repo' -c 'core.editor=cat' rebase
> --onto=HEAD@{upstream} --interactive "$(git -C './client/repo'
> merge-base --fork-point HEAD@{upstream} HEAD)" HEAD

"git rebase <upstream> <branch>" is designed to switch to a different 
branch before rebasing it. If you do not want to switch branches you 
should use "git rebase <upstream>". "<branch>" is expected to be a 
branch name, not a symbolic ref to the branch like "HEAD".

> Replacing `HEAD` with branch name (`"$(git -C './client/repo' branch
> --show-current)"`) works around the problem.

This is working as expected.

"git checkout HEAD" is a no-op so "git rebase <upstream> HEAD" is 
behaving differently to "git checkout HEAD && git rebase <upstream>". We 
could look at changing that but it would be a breaking change for anyone 
relying on the current behavior to detach HEAD before rebasing.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
  2024-09-01 15:26 ` Phillip Wood
@ 2024-09-01 15:42   ` Junio C Hamano
  2024-09-01 22:05     ` Han Jiang
  2024-09-02  9:04     ` Phillip Wood
  0 siblings, 2 replies; 6+ messages in thread
From: Junio C Hamano @ 2024-09-01 15:42 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Han Jiang, git

Phillip Wood <phillip.wood123@gmail.com> writes:

> "git rebase <upstream> <branch>" is designed to switch to a different
> branch before rebasing it. If you do not want to switch branches you
> should use "git rebase <upstream>".

Correct.

> "<branch>" is expected to be a
> branch name, not a symbolic ref to the branch like "HEAD".

I question the correctness of this, though.  The "what to rebase"
argument can be any arbitrary commit-ish, and if it is a name of a
local branch, that branch is rebased.  If it is not, the HEAD is
detached and that detached HEAD state is rebased.  The latter is
handy when you are unsure if you want to really touch the branch.
i.e. "git rebase master topic^0"---this way you'd end up on a
detached HEAD that shows what would happen if you really rebased
"topic" branch, but if you do not like the result, you can just
discard the state by e.g., checking out some branch, and you do not
even contaminate the reflog of the "topic" branch with the record of
this failed exeriment.

I have a mild suspicion that the "rebase" command might have changed
its behaviour since the days back when it was implemented as a shell
script, when the "what to rebase" argument is HEAD, as the most
natural implementation to do this "optionally first switch to it
when the argument is given" in the scripted Porcelain is to actually
run "git checkout HEAD", which should be a somewhat noisy no-op.
Apparently today's "git rebase" does not work that way and seems to
detach HEAD instead and then rebases it.  As you said, that is the
behaviour most users are familiar with and it is probably too late
to change, even if (I didn't check) an ancient version of "rebase"
did not work that way and instead rebased the current branch.

Thanks.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
  2024-09-01 15:42   ` Junio C Hamano
@ 2024-09-01 22:05     ` Han Jiang
  2024-09-02  9:04     ` Phillip Wood
  1 sibling, 0 replies; 6+ messages in thread
From: Han Jiang @ 2024-09-01 22:05 UTC (permalink / raw)
  To: Junio C Hamano, Phillip Wood; +Cc: git

@Phillip Wood @Junio C Hamano
I finally got it. Thank you both for your explanation!

On Mon, Sep 2, 2024 at 3:42 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> > "git rebase <upstream> <branch>" is designed to switch to a different
> > branch before rebasing it. If you do not want to switch branches you
> > should use "git rebase <upstream>".
>
> Correct.
>
> > "<branch>" is expected to be a
> > branch name, not a symbolic ref to the branch like "HEAD".
>
> I question the correctness of this, though.  The "what to rebase"
> argument can be any arbitrary commit-ish, and if it is a name of a
> local branch, that branch is rebased.  If it is not, the HEAD is
> detached and that detached HEAD state is rebased.  The latter is
> handy when you are unsure if you want to really touch the branch.
> i.e. "git rebase master topic^0"---this way you'd end up on a
> detached HEAD that shows what would happen if you really rebased
> "topic" branch, but if you do not like the result, you can just
> discard the state by e.g., checking out some branch, and you do not
> even contaminate the reflog of the "topic" branch with the record of
> this failed exeriment.
>
> I have a mild suspicion that the "rebase" command might have changed
> its behaviour since the days back when it was implemented as a shell
> script, when the "what to rebase" argument is HEAD, as the most
> natural implementation to do this "optionally first switch to it
> when the argument is given" in the scripted Porcelain is to actually
> run "git checkout HEAD", which should be a somewhat noisy no-op.
> Apparently today's "git rebase" does not work that way and seems to
> detach HEAD instead and then rebases it.  As you said, that is the
> behaviour most users are familiar with and it is probably too late
> to change, even if (I didn't check) an ancient version of "rebase"
> did not work that way and instead rebased the current branch.
>
> Thanks.
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
  2024-09-01 15:42   ` Junio C Hamano
  2024-09-01 22:05     ` Han Jiang
@ 2024-09-02  9:04     ` Phillip Wood
  2024-09-03 15:38       ` Junio C Hamano
  1 sibling, 1 reply; 6+ messages in thread
From: Phillip Wood @ 2024-09-02  9:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Han Jiang, git

Hi Junio

On 01/09/2024 16:42, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
> 
>> "<branch>" is expected to be a
>> branch name, not a symbolic ref to the branch like "HEAD".
> 
> I question the correctness of this, though.

Yes as you explain below, it is a bit of an over simplification

> The "what to rebase"
> argument can be any arbitrary commit-ish, and if it is a name of a
> local branch, that branch is rebased.  If it is not, the HEAD is
> detached and that detached HEAD state is rebased.  The latter is
> handy when you are unsure if you want to really touch the branch.
> i.e. "git rebase master topic^0"---this way you'd end up on a
> detached HEAD that shows what would happen if you really rebased
> "topic" branch, but if you do not like the result, you can just
> discard the state by e.g., checking out some branch, and you do not
> even contaminate the reflog of the "topic" branch with the record of
> this failed exeriment.
 >
> I have a mild suspicion that the "rebase" command might have changed
> its behaviour since the days back when it was implemented as a shell
> script, when the "what to rebase" argument is HEAD, as the most
> natural implementation to do this "optionally first switch to it
> when the argument is given" in the scripted Porcelain is to actually
> run "git checkout HEAD", which should be a somewhat noisy no-op.j

It appears to have changed in 0cb06644a5 (rebase [--onto O] A B: omit 
needless checkout, 2008-03-15). That also changed the reflog messages 
written by "git rebase <upstream> <branch>" so that "git checkout @{n}" 
does not see the new branch being checked out.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`
  2024-09-02  9:04     ` Phillip Wood
@ 2024-09-03 15:38       ` Junio C Hamano
  0 siblings, 0 replies; 6+ messages in thread
From: Junio C Hamano @ 2024-09-03 15:38 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Han Jiang, git

Phillip Wood <phillip.wood123@gmail.com> writes:

>> I have a mild suspicion that the "rebase" command might have changed
>> its behaviour since the days back when it was implemented as a shell
>> script, when the "what to rebase" argument is HEAD, as the most
>> natural implementation to do this "optionally first switch to it
>> when the argument is given" in the scripted Porcelain is to actually
>> run "git checkout HEAD", which should be a somewhat noisy no-op.j
>
> It appears to have changed in 0cb06644a5 (rebase [--onto O] A B: omit
> needless checkout, 2008-03-15). That also changed the reflog messages
> written by "git rebase <upstream> <branch>" so that "git checkout
> @{n}" does not see the new branch being checked out.

Thanks for digging.  Even if we now know that it was a regression in
the early times of the current code, it is way too old to change it
now.

Unless we want to do so at a major version bump, but I do not know
if this is big enough to worth writing a release note entry for.
Users who do want to run a trial rebase of the current branch on a
detached HEAD have all been already using HEAD^0 as the <branch>
argument and they have been fine without complaints.

Making it consistent in the opposite direction by changinging "git
checkout HEAD" to detach HEAD is unthinkable.  So what we have is
fine, I would think.

Thanks.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-09-03 15:38 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-01 11:02 `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD` Han Jiang
2024-09-01 15:26 ` Phillip Wood
2024-09-01 15:42   ` Junio C Hamano
2024-09-01 22:05     ` Han Jiang
2024-09-02  9:04     ` Phillip Wood
2024-09-03 15:38       ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).