In cfaff3aac (branch -m: allow renaming a yet-unborn branch, 2020-12-13) we added support for renaming an orphan branch, skipping rename_ref() if the branch being renamed is an orphan branch; checking the current HEAD. However the branch to be renamed can be an orphan branch in a different worktree than the current one, i.e. not the current HEAD. In "copy_or_rename_branch()" we are traversing the worktrees checking if the branch to be renamed is HEAD in any worktree. Let's include also a check for HEAD being NULL, which is the indication of an orphan branch, and use it to extend what we did in cfaff3aac, to all HEADs in the repository. Signed-off-by: Rubén Justo <rjusto@xxxxxxxxx> --- builtin/branch.c | 16 ++++++++++------ t/t3200-branch.sh | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index aecf009993..24cd66bae7 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -489,6 +489,7 @@ static void print_current_branch_name(void) #define IS_BISECTED 1 #define IS_REBASED 2 #define IS_HEAD 4 +#define IS_ORPHAN 8 static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force) { @@ -514,8 +515,11 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int for (int i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; - if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) + if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) { oldref_usage |= IS_HEAD; + if (is_null_oid(&wt->head_oid)) + oldref_usage |= IS_ORPHAN; + } if (!wt->is_detached) continue; @@ -531,8 +535,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int } } - if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) { - if (oldref_usage & IS_HEAD) + if ((copy || !(oldref_usage & IS_HEAD)) && + ((oldref_usage & IS_ORPHAN) || !ref_exists(oldref.buf))) { + if (oldref_usage & IS_ORPHAN) die(_("No commit on branch '%s' yet."), oldname); else die(_("No branch named '%s'."), oldname); @@ -567,8 +572,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int strbuf_addf(&logmsg, "Branch: renamed %s to %s", oldref.buf, newref.buf); - if (!copy && - (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) && + if (!copy && !(oldref_usage & IS_ORPHAN) && rename_ref(oldref.buf, newref.buf, logmsg.buf)) die(_("Branch rename failed")); if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf)) @@ -583,7 +587,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int interpreted_oldname); } - if (!copy) { + if (!copy && (oldref_usage & IS_HEAD)) { /* * Update all per-worktree HEADs pointing at the old ref to * point the new ref. diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 5a169b68d6..5aef00efde 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -279,6 +279,20 @@ test_expect_success 'git branch -M and -C fail on detached HEAD' ' test_cmp expect err ' +test_expect_success 'git branch -m should work with orphan branches' ' + test_when_finished git checkout - && + test_when_finished git worktree remove -f wt && + git worktree add wt --detach && + # rename orphan in another worktreee + git -C wt checkout --orphan orphan-foo-wt && + git branch -m orphan-foo-wt orphan-bar-wt && + test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) && + # rename orphan in the current worktree + git checkout --orphan orphan-foo && + git branch -m orphan-foo orphan-bar && + test orphan-bar=$(git branch --show-current) +' + test_expect_success 'git branch -d on orphan HEAD (merged)' ' test_when_finished git checkout main && git checkout --orphan orphan && -- 2.34.1