Skip to content

Commit d3f2e4a

Browse files
committed
Merge branch 'rj/branch-unborn-in-other-worktrees'
Error messages given when working on an unborn branch that is checked out in another worktree have been improved. * rj/branch-unborn-in-other-worktrees: branch: avoid unnecessary worktrees traversals branch: rename orphan branches in any worktree branch: description for orphan branch errors branch: use get_worktrees() in copy_or_rename_branch() branch: test for failures while renaming branches
2 parents 5bc069e + 3521c63 commit d3f2e4a

File tree

5 files changed

+105
-48
lines changed

5 files changed

+105
-48
lines changed

branch.c

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -840,30 +840,3 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
840840

841841
free_worktrees(worktrees);
842842
}
843-
844-
int replace_each_worktree_head_symref(const char *oldref, const char *newref,
845-
const char *logmsg)
846-
{
847-
int ret = 0;
848-
struct worktree **worktrees = get_worktrees();
849-
int i;
850-
851-
for (i = 0; worktrees[i]; i++) {
852-
struct ref_store *refs;
853-
854-
if (worktrees[i]->is_detached)
855-
continue;
856-
if (!worktrees[i]->head_ref)
857-
continue;
858-
if (strcmp(oldref, worktrees[i]->head_ref))
859-
continue;
860-
861-
refs = get_worktree_ref_store(worktrees[i]);
862-
if (refs_create_symref(refs, "HEAD", newref, logmsg))
863-
ret = error(_("HEAD of working tree %s is not updated"),
864-
worktrees[i]->path);
865-
}
866-
867-
free_worktrees(worktrees);
868-
return ret;
869-
}

branch.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,4 @@ int read_branch_desc(struct strbuf *, const char *branch_name);
155155
*/
156156
void die_if_checked_out(const char *branch, int ignore_current_worktree);
157157

158-
/*
159-
* Update all per-worktree HEADs pointing at the old ref to point the new ref.
160-
* This will be used when renaming a branch. Returns 0 if successful, non-zero
161-
* otherwise.
162-
*/
163-
int replace_each_worktree_head_symref(const char *oldref, const char *newref,
164-
const char *logmsg);
165-
166158
#endif

builtin/branch.c

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,9 @@ static void print_current_branch_name(void)
512512
die(_("HEAD (%s) points outside of refs/heads/"), refname);
513513
}
514514

515-
static void reject_rebase_or_bisect_branch(const char *target)
515+
static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
516+
const char *target)
516517
{
517-
struct worktree **worktrees = get_worktrees();
518518
int i;
519519

520520
for (i = 0; worktrees[i]; i++) {
@@ -531,17 +531,50 @@ static void reject_rebase_or_bisect_branch(const char *target)
531531
die(_("Branch %s is being bisected at %s"),
532532
target, wt->path);
533533
}
534+
}
534535

535-
free_worktrees(worktrees);
536+
/*
537+
* Update all per-worktree HEADs pointing at the old ref to point the new ref.
538+
* This will be used when renaming a branch. Returns 0 if successful, non-zero
539+
* otherwise.
540+
*/
541+
static int replace_each_worktree_head_symref(struct worktree **worktrees,
542+
const char *oldref, const char *newref,
543+
const char *logmsg)
544+
{
545+
int ret = 0;
546+
int i;
547+
548+
for (i = 0; worktrees[i]; i++) {
549+
struct ref_store *refs;
550+
551+
if (worktrees[i]->is_detached)
552+
continue;
553+
if (!worktrees[i]->head_ref)
554+
continue;
555+
if (strcmp(oldref, worktrees[i]->head_ref))
556+
continue;
557+
558+
refs = get_worktree_ref_store(worktrees[i]);
559+
if (refs_create_symref(refs, "HEAD", newref, logmsg))
560+
ret = error(_("HEAD of working tree %s is not updated"),
561+
worktrees[i]->path);
562+
}
563+
564+
return ret;
536565
}
537566

567+
#define IS_HEAD 1
568+
#define IS_ORPHAN 2
569+
538570
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
539571
{
540572
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
541573
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
542574
const char *interpreted_oldname = NULL;
543575
const char *interpreted_newname = NULL;
544-
int recovery = 0;
576+
int recovery = 0, oldref_usage = 0;
577+
struct worktree **worktrees = get_worktrees();
545578

546579
if (strbuf_check_branch_ref(&oldref, oldname)) {
547580
/*
@@ -554,8 +587,19 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
554587
die(_("Invalid branch name: '%s'"), oldname);
555588
}
556589

557-
if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
558-
if (copy && !strcmp(head, oldname))
590+
for (int i = 0; worktrees[i]; i++) {
591+
struct worktree *wt = worktrees[i];
592+
593+
if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
594+
oldref_usage |= IS_HEAD;
595+
if (is_null_oid(&wt->head_oid))
596+
oldref_usage |= IS_ORPHAN;
597+
break;
598+
}
599+
}
600+
601+
if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
602+
if (oldref_usage & IS_HEAD)
559603
die(_("No commit on branch '%s' yet."), oldname);
560604
else
561605
die(_("No branch named '%s'."), oldname);
@@ -570,7 +614,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
570614
else
571615
validate_new_branchname(newname, &newref, force);
572616

573-
reject_rebase_or_bisect_branch(oldref.buf);
617+
reject_rebase_or_bisect_branch(worktrees, oldref.buf);
574618

575619
if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
576620
!skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
@@ -584,8 +628,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
584628
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
585629
oldref.buf, newref.buf);
586630

587-
if (!copy &&
588-
(!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
631+
if (!copy && !(oldref_usage & IS_ORPHAN) &&
589632
rename_ref(oldref.buf, newref.buf, logmsg.buf))
590633
die(_("Branch rename failed"));
591634
if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
@@ -600,8 +643,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
600643
interpreted_oldname);
601644
}
602645

603-
if (!copy &&
604-
replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
646+
if (!copy && (oldref_usage & IS_HEAD) &&
647+
replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
648+
logmsg.buf))
605649
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
606650

607651
strbuf_release(&logmsg);
@@ -616,6 +660,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
616660
strbuf_release(&newref);
617661
strbuf_release(&oldsection);
618662
strbuf_release(&newsection);
663+
free_worktrees(worktrees);
619664
}
620665

621666
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
@@ -834,7 +879,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
834879

835880
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
836881
if (!ref_exists(branch_ref.buf))
837-
error((!argc || !strcmp(head, branch_name))
882+
error((!argc || branch_checked_out(branch_ref.buf))
838883
? _("No commit on branch '%s' yet.")
839884
: _("No branch named '%s'."),
840885
branch_name);
@@ -879,7 +924,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
879924
}
880925

881926
if (!ref_exists(branch->refname)) {
882-
if (!argc || !strcmp(head, branch->name))
927+
if (!argc || branch_checked_out(branch->refname))
883928
die(_("No commit on branch '%s' yet."), branch->name);
884929
die(_("branch '%s' does not exist"), branch->name);
885930
}

t/t3200-branch.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,21 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
239239
git worktree prune
240240
'
241241

242+
test_expect_success 'git branch -M fails if updating any linked working tree fails' '
243+
git worktree add -b baz bazdir1 &&
244+
git worktree add -f bazdir2 baz &&
245+
touch .git/worktrees/bazdir1/HEAD.lock &&
246+
test_must_fail git branch -M baz bam &&
247+
test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
248+
git branch -M bam baz &&
249+
rm .git/worktrees/bazdir1/HEAD.lock &&
250+
touch .git/worktrees/bazdir2/HEAD.lock &&
251+
test_must_fail git branch -M baz bam &&
252+
test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
253+
rm -rf bazdir1 bazdir2 &&
254+
git worktree prune
255+
'
256+
242257
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
243258
git checkout -b baz &&
244259
git worktree add -f bazdir baz &&
@@ -283,6 +298,20 @@ test_expect_success 'git branch -M and -C fail on detached HEAD' '
283298
test_cmp expect err
284299
'
285300

301+
test_expect_success 'git branch -m should work with orphan branches' '
302+
test_when_finished git checkout - &&
303+
test_when_finished git worktree remove -f wt &&
304+
git worktree add wt --detach &&
305+
# rename orphan in another worktreee
306+
git -C wt checkout --orphan orphan-foo-wt &&
307+
git branch -m orphan-foo-wt orphan-bar-wt &&
308+
test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) &&
309+
# rename orphan in the current worktree
310+
git checkout --orphan orphan-foo &&
311+
git branch -m orphan-foo orphan-bar &&
312+
test orphan-bar=$(git branch --show-current)
313+
'
314+
286315
test_expect_success 'git branch -d on orphan HEAD (merged)' '
287316
test_when_finished git checkout main &&
288317
git checkout --orphan orphan &&

t/t3202-show-branch.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,22 @@ test_expect_success 'fatal descriptions on non-existent branch' '
221221
test_cmp expect actual
222222
'
223223

224+
test_expect_success 'error descriptions on orphan branch' '
225+
test_when_finished git worktree remove -f wt &&
226+
git worktree add wt --detach &&
227+
git -C wt checkout --orphan orphan-branch &&
228+
test_branch_op_in_wt() {
229+
test_orphan_error() {
230+
test_must_fail git $* 2>actual &&
231+
test_i18ngrep "No commit on branch .orphan-branch. yet.$" actual
232+
} &&
233+
test_orphan_error -C wt branch $1 $2 && # implicit branch
234+
test_orphan_error -C wt branch $1 orphan-branch $2 && # explicit branch
235+
test_orphan_error branch $1 orphan-branch $2 # different worktree
236+
} &&
237+
test_branch_op_in_wt --edit-description &&
238+
test_branch_op_in_wt --set-upstream-to=ne &&
239+
test_branch_op_in_wt -c new-branch
240+
'
241+
224242
test_done

0 commit comments

Comments
 (0)