{"thread":{"id":"58998","subject":"[PATCH] branch: consider orphans when validate_branchname()","startedAt":"2022-12-23T01:00:34Z","lastAt":"2022-12-23T01:00:34Z","messageCount":1,"participants":["Rubén Justo"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"469483","messageId":"4247f730-5592-de10-a238-5582eac2952d@gmail.com","threadId":"58998","inReplyTo":null,"subject":"[PATCH] branch: consider orphans when validate_branchname()","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2022-12-23T00:58:12Z","receivedAt":"2022-12-23T01:00:34Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"When a branch does not yet have a commit, we call that an orphan branch.\nMore technically, an orphan branch is a HEAD that points to a\nnonexistent ref.\n\nOrphan branches are not normal branches; can only be created, we cannot\nsimply switch to them.  The initial branch in an empty repository is\nan orphan branch.  We can create new orphan branches using\n\"switch --orphan\".\n\nAn orphan branch becomes an ordinary branch when the ref it points to is\ncreated, i.e. when the first commit for the branch is done or the branch\nis reset to an existing commit.\n\nWhen we are asked to create a new branch, orphan or not, we use\nvalidate_branchname() to check if a branch already exists in the\nrepository with the same desired name for the new branch.  In that\nfunction we use ref_exists(), so we are not considering any orphan\nbranches as there is no ref related to an orphan branch.\n\nIf we switch from an orphan branch to a different one, orphan or not,\nthe initial orphan branch is simply lost.  We cannot switch back to it,\nthere is nothing to switch back to.  Therefore, there is no major\nproblem if we only check for valid refs when creating branches, orphans\nor not, as there is no orphan branches to consider in the worktree.\n\nSince 529fef20 (checkout: support checking out into a new working\ndirectory, 2014-12-01) we have a safe way to use multiple worktrees with\na Git repository.\n\nThis allows the possibility of having multiple (ordinary and orphan)\nbranches simultaneously, and since we do not check for orphan branches\nin validate_branchname(), the same orphan branch name can be used\nmultiple times.  Once any one of them becomes an ordinary branch, or a\nnew ordinary branch with the same name is created, all of them will be\n/switched/ to that new branch.\n\nIt also opens the possibility to copy or rename a normal branch to a\nname currently used by an orphan branch, with similar results.\n\nSince 31ad6b61bd (branch: add branch_checked_out() helper, 2022-06-15)\nwe have a convenient way to see if a branch is checked out in any\nworktree.\n\nLet's use branch_checked_out() in validate_branchname() to prevent using\nthe same branch name multiple times, considering orphan branches too.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n branch.c                |  2 +-\n t/t2400-worktree-add.sh | 10 ++++++++++\n t/t3200-branch.sh       | 10 ++++++++++\n 3 files changed, 21 insertions(+), 1 deletion(-)\n\ndiff --git a/branch.c b/branch.c\nindex d182756827..4029721806 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -367,7 +367,7 @@ int validate_branchname(const char *name, struct strbuf *ref)\n \tif (strbuf_check_branch_ref(ref, name))\n \t\tdie(_(\"'%s' is not a valid branch name\"), name);\n \n-\treturn ref_exists(ref->buf);\n+\treturn ref_exists(ref->buf) || branch_checked_out(ref->buf);\n }\n \n static int initialized_checked_out_branches;\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex d587e0b20d..f1e4b605da 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -118,6 +118,16 @@ test_expect_success '\"add\" worktree creating new branch' '\n \t)\n '\n \n+test_expect_success 'do not allow multiple worktrees with same orphan branch' '\n+\ttest_when_finished \"git worktree remove --force detached-wt-A\" &&\n+\ttest_when_finished \"git worktree remove --force detached-wt-B\" &&\n+\tgit worktree add --detach detached-wt-A &&\n+\tgit -C detached-wt-A checkout --orphan orphan-branch &&\n+\tgit worktree add --detach detached-wt-B &&\n+\ttest_must_fail git -C detached-wt-B checkout --orphan orphan-branch &&\n+\ttest_must_fail git checkout --orphan orphan-branch\n+'\n+\n test_expect_success 'die the same branch is already checked out' '\n \t(\n \t\tcd here &&\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 5a169b68d6..68bc579fff 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -192,6 +192,16 @@ test_expect_success 'git branch -M foo bar should fail when bar is checked out i\n \ttest_must_fail git branch -M bar wt\n '\n \n+test_expect_success 'git branch -M/-C bar should fail when destination exists as orphan' '\n+\ttest_when_finished \"git worktree remove --force orphan-worktree\" &&\n+\tgit worktree add --detach orphan-worktree &&\n+\tgit -C orphan-worktree checkout --orphan orphan-branch &&\n+\ttest_must_fail git checkout --orphan orphan-branch &&\n+\ttest_must_fail git branch orphan-branch &&\n+\ttest_must_fail git branch -M orphan-branch &&\n+\ttest_must_fail git branch -C orphan-branch\n+'\n+\n test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '\n \tgit checkout -b baz &&\n \tgit branch bam &&\n-- \n2.36.1\n\n"}]}