{"thread":{"id":"62032","subject":"`git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","startedAt":"2024-09-01T11:02:20Z","lastAt":"2024-09-03T15:38:37Z","messageCount":6,"participants":["Han Jiang","Phillip Wood","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"501955","messageId":"CANrWfmSY1F4UB2QSjN8XKY7Kwx6FL8SOrz_OadZ4u8XYDpZfBg@mail.gmail.com","threadId":"62032","inReplyTo":null,"subject":"`git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Han Jiang","fromEmail":"jhcarl0814@gmail.com","sentAt":"2024-09-01T11:02:07Z","receivedAt":"2024-09-01T11:02:20Z","isPatch":false,"sender":{"key":"jhcarl0814@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5171262?v=4"},"body":"Thank you for filling out a Git bug report!\nPlease answer the following questions to help us understand your issue.\n\nWhat did you do before the bug happened? (Steps to reproduce your issue)\n\ncd '/'; cd '/'; rm --force --recursive -- './test_git'; mkdir \"$_\"; cd \"$_\";\nmkdir --parents -- './server' './client';\ngit -C './server' init --bare './repo.git'\necho '1' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\necho '2' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\ngit -C './server/repo.git' log --graph --all --patch\n\ngit -C './client' clone '../server/repo.git' './repo'\nbranch_default_path=\"$(git -C './client/repo' symbolic-ref HEAD)\"\nbranch_default_name=\"${branch_default_path#'refs/heads/'}\"\necho '3' >'./client/repo/file'; git -C './client/repo' add './file';\ngit -C './client/repo' commit -m \"$((++number))\"\ngit -C './client/repo' log --graph --all --patch\n\ngit -C './server/repo.git' reset --soft HEAD~1\necho '4' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\ngit -C './server/repo.git' log --graph --all --patch\n\ngit -C './client/repo' fetch --all\ngit -C './client/repo' log --graph --all --patch\ngit -C './client/repo' -c 'core.editor=cat' rebase\n--onto=HEAD@{upstream} --interactive \"$(git -C './client/repo'\nmerge-base --fork-point HEAD@{upstream} HEAD)\" HEAD\ngit -C './client/repo' log --graph --all --patch\necho '5' >'./client/repo/file'; git -C './client/repo' add './file';\ngit -C './client/repo' -c 'core.editor=:' rebase --continue\ngit -C './client/repo' log --graph --all --patch\ngit -C './client/repo' checkout -B \"$branch_default_name\"\ngit -C './client/repo' log --graph --all --patch\n\nWhat did you expect to happen? (Expected behavior)\n\nHEAD points to GIT_DEFAULT_BRANCH and GIT_DEFAULT_BRANCH points to tip\nof origin's GIT_DEFAULT_BRANCH when rebase completes.\n\nWhat happened instead? (Actual behavior)\n\nHEAD is detached and *HEAD is not moved when rebase completes, so `git\ncheckout -B` is needed.\n\nWhat's different between what you expected and what actually happened?\n\nAnything else you want to add:\n\nReplacing `HEAD` with branch name (`\"$(git -C './client/repo' branch\n--show-current)\"`) works around the problem.\ncd '/'; cd '/'; rm --force --recursive -- './test_git'; mkdir \"$_\"; cd \"$_\";\nmkdir --parents -- './server' './client';\ngit -C './server' init --bare './repo.git'\necho '1' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\necho '2' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\ngit -C './server/repo.git' log --graph --all --patch\n\ngit -C './client' clone '../server/repo.git' './repo'\necho '3' >'./client/repo/file'; git -C './client/repo' add './file';\ngit -C './client/repo' commit -m \"$((++number))\"\ngit -C './client/repo' log --graph --all --patch\n\ngit -C './server/repo.git' reset --soft HEAD~1\necho '4' >'./file'; git --git-dir='./server/repo.git' --work-tree='.'\nadd './file'; git --git-dir='./server/repo.git' --work-tree='.' commit\n-m \"$((++number))\"\ngit -C './server/repo.git' log --graph --all --patch\n\ngit -C './client/repo' fetch --all\ngit -C './client/repo' log --graph --all --patch\ngit -C './client/repo' -c 'core.editor=cat' rebase\n--onto=HEAD@{upstream} --interactive \"$(git -C './client/repo'\nmerge-base --fork-point HEAD@{upstream} HEAD)\" \"$(git -C\n'./client/repo' branch --show-current)\"\ngit -C './client/repo' log --graph --all --patch\necho '5' >'./client/repo/file'; git -C './client/repo' add './file';\ngit -C './client/repo' -c 'core.editor=:' rebase --continue\ngit -C './client/repo' log --graph --all --patch\n\nPlease review the rest of the bug report below.\nYou can delete any lines you don't wish to share.\n\n\n[System Info]\ngit version:\ngit version 2.46.0.windows.1\ncpu: x86_64\nbuilt from commit: 2e6a859ffc0471f60f79c1256f766042b0d5d17d\nsizeof-long: 4\nsizeof-size_t: 8\nshell-path: D:/git-sdk-64-build-installers/usr/bin/sh\nfeature: fsmonitor--daemon\nlibcurl: 8.9.0\nOpenSSL: OpenSSL 3.2.2 4 Jun 2024\nzlib: 1.3.1\nuname: Windows 10.0 22631\ncompiler info: gnuc: 14.1\nlibc info: no libc information available\n$SHELL (typically, interactive shell): C:\\Program Files\\Git\\usr\\bin\\bash.exe\n\n\n[Enabled Hooks]\nnot run from a git repository - no hooks to show\n"},{"id":"501957","messageId":"61a4fcc1-1dd8-48a4-a1d4-0201232c9b26@gmail.com","threadId":"62032","inReplyTo":"CANrWfmSY1F4UB2QSjN8XKY7Kwx6FL8SOrz_OadZ4u8XYDpZfBg@mail.gmail.com","subject":"Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-01T15:26:27Z","receivedAt":"2024-09-01T15:26:31Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 01/09/2024 12:02, Han Jiang wrote:\n> What did you do before the bug happened? (Steps to reproduce your issue)\n\n> git -C './client/repo' -c 'core.editor=cat' rebase\n> --onto=HEAD@{upstream} --interactive \"$(git -C './client/repo'\n> merge-base --fork-point HEAD@{upstream} HEAD)\" HEAD\n\n\"git rebase <upstream> <branch>\" is designed to switch to a different \nbranch before rebasing it. If you do not want to switch branches you \nshould use \"git rebase <upstream>\". \"<branch>\" is expected to be a \nbranch name, not a symbolic ref to the branch like \"HEAD\".\n\n> Replacing `HEAD` with branch name (`\"$(git -C './client/repo' branch\n> --show-current)\"`) works around the problem.\n\nThis is working as expected.\n\n\"git checkout HEAD\" is a no-op so \"git rebase <upstream> HEAD\" is \nbehaving differently to \"git checkout HEAD && git rebase <upstream>\". We \ncould look at changing that but it would be a breaking change for anyone \nrelying on the current behavior to detach HEAD before rebasing.\n\nBest Wishes\n\nPhillip\n"},{"id":"501958","messageId":"xmqq1q23pegz.fsf@gitster.g","threadId":"62032","inReplyTo":"61a4fcc1-1dd8-48a4-a1d4-0201232c9b26@gmail.com","subject":"Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-01T15:42:52Z","receivedAt":"2024-09-01T15:42:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> \"git rebase <upstream> <branch>\" is designed to switch to a different\n> branch before rebasing it. If you do not want to switch branches you\n> should use \"git rebase <upstream>\".\n\nCorrect.\n\n> \"<branch>\" is expected to be a\n> branch name, not a symbolic ref to the branch like \"HEAD\".\n\nI question the correctness of this, though.  The \"what to rebase\"\nargument can be any arbitrary commit-ish, and if it is a name of a\nlocal branch, that branch is rebased.  If it is not, the HEAD is\ndetached and that detached HEAD state is rebased.  The latter is\nhandy when you are unsure if you want to really touch the branch.\ni.e. \"git rebase master topic^0\"---this way you'd end up on a\ndetached HEAD that shows what would happen if you really rebased\n\"topic\" branch, but if you do not like the result, you can just\ndiscard the state by e.g., checking out some branch, and you do not\neven contaminate the reflog of the \"topic\" branch with the record of\nthis failed exeriment.\n\nI have a mild suspicion that the \"rebase\" command might have changed\nits behaviour since the days back when it was implemented as a shell\nscript, when the \"what to rebase\" argument is HEAD, as the most\nnatural implementation to do this \"optionally first switch to it\nwhen the argument is given\" in the scripted Porcelain is to actually\nrun \"git checkout HEAD\", which should be a somewhat noisy no-op.\nApparently today's \"git rebase\" does not work that way and seems to\ndetach HEAD instead and then rebases it.  As you said, that is the\nbehaviour most users are familiar with and it is probably too late\nto change, even if (I didn't check) an ancient version of \"rebase\"\ndid not work that way and instead rebased the current branch.\n\nThanks.\n\n"},{"id":"501966","messageId":"CANrWfmSgAB_7fLYE1sjYjq+HtjaW-UKne6KezATWkipr2MjFbA@mail.gmail.com","threadId":"62032","inReplyTo":"xmqq1q23pegz.fsf@gitster.g","subject":"Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Han Jiang","fromEmail":"jhcarl0814@gmail.com","sentAt":"2024-09-01T22:05:51Z","receivedAt":"2024-09-01T22:06:04Z","isPatch":false,"sender":{"key":"jhcarl0814@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5171262?v=4"},"body":"@Phillip Wood @Junio C Hamano\nI finally got it. Thank you both for your explanation!\n\nOn Mon, Sep 2, 2024 at 3:42 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n>\n> > \"git rebase <upstream> <branch>\" is designed to switch to a different\n> > branch before rebasing it. If you do not want to switch branches you\n> > should use \"git rebase <upstream>\".\n>\n> Correct.\n>\n> > \"<branch>\" is expected to be a\n> > branch name, not a symbolic ref to the branch like \"HEAD\".\n>\n> I question the correctness of this, though.  The \"what to rebase\"\n> argument can be any arbitrary commit-ish, and if it is a name of a\n> local branch, that branch is rebased.  If it is not, the HEAD is\n> detached and that detached HEAD state is rebased.  The latter is\n> handy when you are unsure if you want to really touch the branch.\n> i.e. \"git rebase master topic^0\"---this way you'd end up on a\n> detached HEAD that shows what would happen if you really rebased\n> \"topic\" branch, but if you do not like the result, you can just\n> discard the state by e.g., checking out some branch, and you do not\n> even contaminate the reflog of the \"topic\" branch with the record of\n> this failed exeriment.\n>\n> I have a mild suspicion that the \"rebase\" command might have changed\n> its behaviour since the days back when it was implemented as a shell\n> script, when the \"what to rebase\" argument is HEAD, as the most\n> natural implementation to do this \"optionally first switch to it\n> when the argument is given\" in the scripted Porcelain is to actually\n> run \"git checkout HEAD\", which should be a somewhat noisy no-op.\n> Apparently today's \"git rebase\" does not work that way and seems to\n> detach HEAD instead and then rebases it.  As you said, that is the\n> behaviour most users are familiar with and it is probably too late\n> to change, even if (I didn't check) an ancient version of \"rebase\"\n> did not work that way and instead rebased the current branch.\n>\n> Thanks.\n>\n"},{"id":"501971","messageId":"981c8e52-e51d-4aab-a970-bb41315e02cb@gmail.com","threadId":"62032","inReplyTo":"xmqq1q23pegz.fsf@gitster.g","subject":"Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-02T09:04:24Z","receivedAt":"2024-09-02T09:04:29Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Junio\n\nOn 01/09/2024 16:42, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n>> \"<branch>\" is expected to be a\n>> branch name, not a symbolic ref to the branch like \"HEAD\".\n> \n> I question the correctness of this, though.\n\nYes as you explain below, it is a bit of an over simplification\n\n> The \"what to rebase\"\n> argument can be any arbitrary commit-ish, and if it is a name of a\n> local branch, that branch is rebased.  If it is not, the HEAD is\n> detached and that detached HEAD state is rebased.  The latter is\n> handy when you are unsure if you want to really touch the branch.\n> i.e. \"git rebase master topic^0\"---this way you'd end up on a\n> detached HEAD that shows what would happen if you really rebased\n> \"topic\" branch, but if you do not like the result, you can just\n> discard the state by e.g., checking out some branch, and you do not\n> even contaminate the reflog of the \"topic\" branch with the record of\n> this failed exeriment.\n >\n> I have a mild suspicion that the \"rebase\" command might have changed\n> its behaviour since the days back when it was implemented as a shell\n> script, when the \"what to rebase\" argument is HEAD, as the most\n> natural implementation to do this \"optionally first switch to it\n> when the argument is given\" in the scripted Porcelain is to actually\n> run \"git checkout HEAD\", which should be a somewhat noisy no-op.j\n\nIt appears to have changed in 0cb06644a5 (rebase [--onto O] A B: omit \nneedless checkout, 2008-03-15). That also changed the reflog messages \nwritten by \"git rebase <upstream> <branch>\" so that \"git checkout @{n}\" \ndoes not see the new branch being checked out.\n\nBest Wishes\n\nPhillip\n"},{"id":"502040","messageId":"xmqq4j6woih6.fsf@gitster.g","threadId":"62032","inReplyTo":"981c8e52-e51d-4aab-a970-bb41315e02cb@gmail.com","subject":"Re: `git rebase (--no-fork-point) --onto=<newbase> [<upstream> [<branch>]]` leaves HEAD detached and *HEAD not moved when <branch> is exactly `HEAD`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-03T15:38:29Z","receivedAt":"2024-09-03T15:38:37Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> I have a mild suspicion that the \"rebase\" command might have changed\n>> its behaviour since the days back when it was implemented as a shell\n>> script, when the \"what to rebase\" argument is HEAD, as the most\n>> natural implementation to do this \"optionally first switch to it\n>> when the argument is given\" in the scripted Porcelain is to actually\n>> run \"git checkout HEAD\", which should be a somewhat noisy no-op.j\n>\n> It appears to have changed in 0cb06644a5 (rebase [--onto O] A B: omit\n> needless checkout, 2008-03-15). That also changed the reflog messages\n> written by \"git rebase <upstream> <branch>\" so that \"git checkout\n> @{n}\" does not see the new branch being checked out.\n\nThanks for digging.  Even if we now know that it was a regression in\nthe early times of the current code, it is way too old to change it\nnow.\n\nUnless we want to do so at a major version bump, but I do not know\nif this is big enough to worth writing a release note entry for.\nUsers who do want to run a trial rebase of the current branch on a\ndetached HEAD have all been already using HEAD^0 as the <branch>\nargument and they have been fine without complaints.\n\nMaking it consistent in the opposite direction by changinging \"git\ncheckout HEAD\" to detach HEAD is unthinkable.  So what we have is\nfine, I would think.\n\nThanks.\n"}]}