{"thread":{"id":"59973","subject":"[rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","startedAt":"2023-07-12T00:46:12Z","lastAt":"2023-07-21T21:53:20Z","messageCount":6,"participants":["Josh Soref","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"479400","messageId":"CACZqfqAB7zyn56+NOL=E8Y3bXNiQnJdmVXgzsnj78C-Anw-h2Q@mail.gmail.com","threadId":"59973","inReplyTo":null,"subject":"[rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","fromName":"Josh Soref","fromEmail":"jsoref@gmail.com","sentAt":"2023-07-12T00:45:54Z","receivedAt":"2023-07-12T00:46:12Z","isPatch":false,"sender":{"key":"jsoref@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2119212?v=4"},"body":"Consider this series of unfortunate commands:\n```sh\n% (cd $(mktemp -d); git init; (touch a; git add a; git commit -m a;\ntouch b; git add b; git commit -m b; git bisect start; git branch next\nHEAD~; git checkout next ) 2>/dev/null >/dev/null; git log --oneline\n-1 HEAD; git branch -f main HEAD;)\nInitialized empty Git repository in\n/private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf/.git/\naccc238 (HEAD -> next) a\nfatal: cannot force update the branch 'main' checked out at\n'/private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf'\n```\n\nIt's true git in the repository does have a pin of sorts for the\n`main` branch and that `git-rebase` would be very upset if the\nbranch's location were changed, but as an end user, the `main` branch\nis not checked out, the `next` branch is checked out.\n"},{"id":"479407","messageId":"xmqqbkghw4qd.fsf@gitster.g","threadId":"59973","inReplyTo":"CACZqfqAB7zyn56+NOL=E8Y3bXNiQnJdmVXgzsnj78C-Anw-h2Q@mail.gmail.com","subject":"Re: [rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-07-12T15:04:42Z","receivedAt":"2023-07-12T15:04:58Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Soref <jsoref@gmail.com> writes:\n\n> Consider this series of unfortunate commands:\n> ```sh\n> % (cd $(mktemp -d); git init; (touch a; git add a; git commit -m a;\n> touch b; git add b; git commit -m b; git bisect start; git branch next\n> HEAD~; git checkout next ) 2>/dev/null >/dev/null; git log --oneline\n> -1 HEAD; git branch -f main HEAD;)\n> Initialized empty Git repository in\n> /private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf/.git/\n> accc238 (HEAD -> next) a\n> fatal: cannot force update the branch 'main' checked out at\n> '/private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf'\n> ```\n>\n> It's true git in the repository does have a pin of sorts for the\n> `main` branch and that `git-rebase` would be very upset if the\n> branch's location were changed, but as an end user, the `main` branch\n> is not checked out, the `next` branch is checked out.\n\nFair enough.  Perhaps \"cannot force update the branch 'main' in use\"\nwould alleviate the confusion?\n\n"},{"id":"479421","messageId":"CACZqfqDE-SCOqVX9a8KCLAn1zSVbYxuCeU8Y=3-nhj5wbL=AWQ@mail.gmail.com","threadId":"59973","inReplyTo":"xmqqbkghw4qd.fsf@gitster.g","subject":"Re: [rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","fromName":"Josh Soref","fromEmail":"jsoref@gmail.com","sentAt":"2023-07-12T20:33:22Z","receivedAt":"2023-07-12T20:33:38Z","isPatch":false,"sender":{"key":"jsoref@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2119212?v=4"},"body":"Josh Soref writes:\n> fatal: cannot force update the branch 'main' checked out at\n> '/private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf'\n\n> It's true git in the repository does have a pin of sorts for the\n> `main` branch and that `git-rebase` would be very upset if the\n> branch's location were changed, but as an end user, the `main` branch\n> is not checked out, the `next` branch is checked out.\n\nJunio C Hamano wrote:\n> Fair enough.  Perhaps \"cannot force update the branch 'main' in use\"\n> would alleviate the confusion?\n\nThat would be better.\n\nEven better would be to tell me what's using it (rebase). I'm not sure\nhow possible that is.\n"},{"id":"479424","messageId":"xmqq5y6osvmc.fsf@gitster.g","threadId":"59973","inReplyTo":"CACZqfqDE-SCOqVX9a8KCLAn1zSVbYxuCeU8Y=3-nhj5wbL=AWQ@mail.gmail.com","subject":"Re: [rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-07-12T20:49:47Z","receivedAt":"2023-07-12T20:49:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Soref <jsoref@gmail.com> writes:\n\n> Josh Soref writes:\n>> fatal: cannot force update the branch 'main' checked out at\n>> '/private/var/folders/r3/n29fz25x72x191fdv6mhhr3m0000gp/T/tmp.fGD64HAf'\n>\n>> It's true git in the repository does have a pin of sorts for the\n>> `main` branch and that `git-rebase` would be very upset if the\n>> branch's location were changed, but as an end user, the `main` branch\n>> is not checked out, the `next` branch is checked out.\n>\n> Junio C Hamano wrote:\n>> Fair enough.  Perhaps \"cannot force update the branch 'main' in use\"\n>> would alleviate the confusion?\n>\n> That would be better.\n>\n> Even better would be to tell me what's using it (rebase). I'm not sure\n> how possible that is.\n\nWhen branch.c:prepare_checked_out() notices which branch is in use\nin what way in which worktree, it does check many conditions (e.g.\n\"in this worktree, is a rebase in progress and if so which branch is\nbeing affected?\") but the information is lost there to only record a\nmapping from branch names to worktree location.  So \"it is in use\nover there\" with exact location is given, but \"how it is used\" is\nnot recorded.\n\nIt shouldn't be a rocket surgery to teach the function to leave that\ninformation, but I am not offhand sure how valuable it would be to\ndo so.  After all, once you learn which worktree of yours is using\nthe branch you wanted to touch, you'll either know already or it\nwould be easy for you to find out what you have been doing in that\nother worktree anyway, no?\n\n"},{"id":"479426","messageId":"xmqqy1jkrcmg.fsf_-_@gitster.g","threadId":"59973","inReplyTo":"xmqq5y6osvmc.fsf@gitster.g","subject":"Re* [rebase] `fatal: cannot force update the branch ... checkout out at ...` is confusing when it isn't active","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-07-12T22:25:27Z","receivedAt":"2023-07-12T22:25:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> It shouldn't be a rocket surgery to teach the function to leave that\n> information, but I am not offhand sure how valuable it would be to\n> do so.  After all, once you learn which worktree of yours is using\n> the branch you wanted to touch, you'll either know already or it\n> would be easy for you to find out what you have been doing in that\n> other worktree anyway, no?\n\nSo, before we forget, let's make a minimum patch and let the list\narchive it.\n\n----- >8 --------- >8 --------- >8 --------- >8 --------- >8 -----\n\nSubject: [PATCH] branch: update the message to refuse touching a branch in-use\n\nThe \"git branch -f\" command can refuse to force-update a branch that\nis in use in another worktree.  The original rationale for this\nbehaviour was that updating a branch that is checked out in another\nworktree, without making a matching change to the index and the\nworking tree files in that worktree, will lead to a very confused\nuser.  \"git diff HEAD\" will no longer give a useful patch, because\nHEAD is a commit unrelated to what the index and the working tree in\nthe worktree were based on, for example.\n\nThese days, however, the same mechanism also protects branches that\nare being rebased or bisected, and the same machanism is expected to\nbe the right place to add more checks, when we decide to protect\nbranches undergoing other kinds of operations.  We forgot to rethink\nthe messaging, which originally said that we are refusing to touch\nthe branch because it is \"checked out\" elsewhere, when d2ba271a\n(branch: check for bisects and rebases, 2022-06-14) started to\nprotect branches that are being rebased or bisected.\n\nThe spirit of the check is that we do not want to disrupt the use of\nthe same branch in other worktrees.  Let's reword the message slightly\nto say that the branch is \"in use\", instead of \"checked out\".\n\nWe could teach the branch.c:prepare_checked_out_branches() function\nto remember why it decided that a particular branch needs protecting\n(i.e. was it because it was checked out?  being bisected?  something\nelse?) in addition to which worktree the branch was in use, and use\nthat in the error message to say \"you cannot force update this\nbranch because it is being bisected in the worktree X\", etc., but it\nis dubious that such extra complexity is worth it.  The message\nalready tells which directory the worktree in question is, and it\nshould be just a \"chdir\" away for the user to find out what state it\nis in, if the user felt curious enough.  So let's not go there yet.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n branch.c                  |  2 +-\n t/t2407-worktree-heads.sh | 10 +++++-----\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex cdf70b0ef0..6cb3b4c56d 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -471,7 +471,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)\n \n \tif ((path = branch_checked_out(ref->buf)))\n \t\tdie(_(\"cannot force update the branch '%s' \"\n-\t\t      \"checked out at '%s'\"),\n+\t\t      \"in use at '%s'\"),\n \t\t    ref->buf + strlen(\"refs/heads/\"), path);\n \n \treturn 1;\ndiff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh\nindex 019a40df2c..d005ce2f20 100755\n--- a/t/t2407-worktree-heads.sh\n+++ b/t/t2407-worktree-heads.sh\n@@ -58,7 +58,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in bisect' '\n \tgit -C wt-4 bisect good wt-1 &&\n \n \ttest_must_fail git branch -f wt-4 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-4'\\'' checked out at.*wt-4\" err\n+\tgrep \"cannot force update the branch '\\''wt-4'\\'' in use at.*wt-4\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' '\n@@ -68,7 +68,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (app\n \ttest_must_fail git -C wt-2 rebase --apply conflict-2 &&\n \n \ttest_must_fail git branch -f wt-2 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-2'\\'' checked out at.*wt-2\" err\n+\tgrep \"cannot force update the branch '\\''wt-2'\\'' in use at.*wt-2\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' '\n@@ -78,7 +78,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (mer\n \ttest_must_fail git -C wt-2 rebase conflict-2 &&\n \n \ttest_must_fail git branch -f wt-2 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-2'\\'' checked out at.*wt-2\" err\n+\tgrep \"cannot force update the branch '\\''wt-2'\\'' in use at.*wt-2\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' '\n@@ -90,7 +90,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with\n \tfor i in 3 4\n \tdo\n \t\ttest_must_fail git branch -f can-be-updated HEAD 2>err &&\n-\t\tgrep \"cannot force update the branch '\\''can-be-updated'\\'' checked out at.*wt-3\" err ||\n+\t\tgrep \"cannot force update the branch '\\''can-be-updated'\\'' in use at.*wt-3\" err ||\n \t\t\treturn 1\n \tdone\n '\n@@ -150,7 +150,7 @@ test_expect_success 'refuse to overwrite when in error states' '\n \tfor i in 1 2\n \tdo\n \t\ttest_must_fail git branch -f fake-$i HEAD 2>err &&\n-\t\tgrep \"cannot force update the branch '\\''fake-$i'\\'' checked out at\" err ||\n+\t\tgrep \"cannot force update the branch '\\''fake-$i'\\'' in use at\" err ||\n \t\t\treturn 1\n \tdone\n '\n-- \n2.41.0-327-gaa9166bcc0\n\n"},{"id":"479771","messageId":"xmqqr0p1szhz.fsf_-_@gitster.g","threadId":"59973","inReplyTo":"xmqqy1jkrcmg.fsf_-_@gitster.g","subject":"[PATCH v2] branch: update the message to refuse touching a branch in-use","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-07-21T21:53:12Z","receivedAt":"2023-07-21T21:53:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The \"git branch -f\" command can refuse to force-update a branch that\nis used by another worktree.  The original rationale for this\nbehaviour was that updating a branch that is checked out in another\nworktree, without making a matching change to the index and the\nworking tree files in that worktree, will lead to a very confused\nuser.  \"git diff HEAD\" will no longer give a useful patch, because\nHEAD is a commit unrelated to what the index and the working tree in\nthe worktree were based on, for example.\n\nThese days, the same mechanism also protects branches that are being\nrebased or bisected, and the same machanism is expected to be the\nright place to add more checks, when we decide to protect branches\nundergoing other kinds of operations.  We however forgot to rethink\nthe messaging, which originally said that we are refusing to touch\nthe branch because it is \"checked out\" elsewhere, when d2ba271a\n(branch: check for bisects and rebases, 2022-06-14) started to\nprotect branches that are being rebased or bisected.\n\nThe spirit of the check has always been that we do not want to\ndisrupt the use of the same branch in other worktrees.  Let's reword\nthe message slightly to say that the branch is \"used by\" another\nworktree, instead of \"checked out\".\n\nWe could teach the branch.c:prepare_checked_out_branches() function\nto remember why it decided that a particular branch needs protecting\n(i.e. was it because it was checked out?  being bisected?  something\nelse?) in addition to which worktree the branch was in use, and use\nthat in the error message to say \"you cannot force update this\nbranch because it is being bisected in the worktree X\", etc., but it\nis dubious that such extra complexity is worth it.  The message\nalready tells which directory the worktree in question is, and it\nshould be just a \"chdir\" away for the user to find out what state it\nis in, if the user felt curious enough.  So let's not go there yet.\n\nHelped-by: Josh Sref <jsoref@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n * Instead of saying \"branch in use\", use a bit more natural \"branch\n   is used by ...\".\n\n branch.c                  |  2 +-\n t/t2407-worktree-heads.sh | 10 +++++-----\n 2 files changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex cdf70b0ef0..3e4684f79f 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -471,7 +471,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)\n \n \tif ((path = branch_checked_out(ref->buf)))\n \t\tdie(_(\"cannot force update the branch '%s' \"\n-\t\t      \"checked out at '%s'\"),\n+\t\t      \"used by worktree at '%s'\"),\n \t\t    ref->buf + strlen(\"refs/heads/\"), path);\n \n \treturn 1;\ndiff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh\nindex 019a40df2c..469443d8ae 100755\n--- a/t/t2407-worktree-heads.sh\n+++ b/t/t2407-worktree-heads.sh\n@@ -58,7 +58,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in bisect' '\n \tgit -C wt-4 bisect good wt-1 &&\n \n \ttest_must_fail git branch -f wt-4 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-4'\\'' checked out at.*wt-4\" err\n+\tgrep \"cannot force update the branch '\\''wt-4'\\'' used by worktree at.*wt-4\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' '\n@@ -68,7 +68,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (app\n \ttest_must_fail git -C wt-2 rebase --apply conflict-2 &&\n \n \ttest_must_fail git branch -f wt-2 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-2'\\'' checked out at.*wt-2\" err\n+\tgrep \"cannot force update the branch '\\''wt-2'\\'' used by worktree at.*wt-2\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' '\n@@ -78,7 +78,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (mer\n \ttest_must_fail git -C wt-2 rebase conflict-2 &&\n \n \ttest_must_fail git branch -f wt-2 HEAD 2>err &&\n-\tgrep \"cannot force update the branch '\\''wt-2'\\'' checked out at.*wt-2\" err\n+\tgrep \"cannot force update the branch '\\''wt-2'\\'' used by worktree at.*wt-2\" err\n '\n \n test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' '\n@@ -90,7 +90,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with\n \tfor i in 3 4\n \tdo\n \t\ttest_must_fail git branch -f can-be-updated HEAD 2>err &&\n-\t\tgrep \"cannot force update the branch '\\''can-be-updated'\\'' checked out at.*wt-3\" err ||\n+\t\tgrep \"cannot force update the branch '\\''can-be-updated'\\'' used by worktree at.*wt-3\" err ||\n \t\t\treturn 1\n \tdone\n '\n@@ -150,7 +150,7 @@ test_expect_success 'refuse to overwrite when in error states' '\n \tfor i in 1 2\n \tdo\n \t\ttest_must_fail git branch -f fake-$i HEAD 2>err &&\n-\t\tgrep \"cannot force update the branch '\\''fake-$i'\\'' checked out at\" err ||\n+\t\tgrep \"cannot force update the branch '\\''fake-$i'\\'' used by worktree at\" err ||\n \t\t\treturn 1\n \tdone\n '\n-- \n2.41.0-376-gcba07a324d\n\n"}]}