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
This commit is contained in:
Junio C Hamano
2023-05-15 13:59:02 -07:00
5 changed files with 106 additions and 49 deletions

View File

@ -512,9 +512,9 @@ static void print_current_branch_name(void)
die(_("HEAD (%s) points outside of refs/heads/"), refname);
}
static void reject_rebase_or_bisect_branch(const char *target)
static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
const char *target)
{
struct worktree **worktrees = get_worktrees();
int i;
for (i = 0; worktrees[i]; i++) {
@ -531,17 +531,50 @@ static void reject_rebase_or_bisect_branch(const char *target)
die(_("Branch %s is being bisected at %s"),
target, wt->path);
}
free_worktrees(worktrees);
}
/*
* Update all per-worktree HEADs pointing at the old ref to point the new ref.
* This will be used when renaming a branch. Returns 0 if successful, non-zero
* otherwise.
*/
static int replace_each_worktree_head_symref(struct worktree **worktrees,
const char *oldref, const char *newref,
const char *logmsg)
{
int ret = 0;
int i;
for (i = 0; worktrees[i]; i++) {
struct ref_store *refs;
if (worktrees[i]->is_detached)
continue;
if (!worktrees[i]->head_ref)
continue;
if (strcmp(oldref, worktrees[i]->head_ref))
continue;
refs = get_worktree_ref_store(worktrees[i]);
if (refs_create_symref(refs, "HEAD", newref, logmsg))
ret = error(_("HEAD of working tree %s is not updated"),
worktrees[i]->path);
}
return ret;
}
#define IS_HEAD 1
#define IS_ORPHAN 2
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
const char *interpreted_oldname = NULL;
const char *interpreted_newname = NULL;
int recovery = 0;
int recovery = 0, oldref_usage = 0;
struct worktree **worktrees = get_worktrees();
if (strbuf_check_branch_ref(&oldref, oldname)) {
/*
@ -554,8 +587,19 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
die(_("Invalid branch name: '%s'"), oldname);
}
if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
if (copy && !strcmp(head, oldname))
for (int i = 0; worktrees[i]; i++) {
struct worktree *wt = worktrees[i];
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;
break;
}
}
if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
if (oldref_usage & IS_HEAD)
die(_("No commit on branch '%s' yet."), oldname);
else
die(_("No branch named '%s'."), oldname);
@ -570,7 +614,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
else
validate_new_branchname(newname, &newref, force);
reject_rebase_or_bisect_branch(oldref.buf);
reject_rebase_or_bisect_branch(worktrees, oldref.buf);
if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
!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
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))
@ -600,8 +643,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
interpreted_oldname);
}
if (!copy &&
replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
if (!copy && (oldref_usage & IS_HEAD) &&
replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
logmsg.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
strbuf_release(&logmsg);
@ -616,6 +660,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
strbuf_release(&newref);
strbuf_release(&oldsection);
strbuf_release(&newsection);
free_worktrees(worktrees);
}
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
@ -834,7 +879,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
if (!ref_exists(branch_ref.buf))
error((!argc || !strcmp(head, branch_name))
error((!argc || branch_checked_out(branch_ref.buf))
? _("No commit on branch '%s' yet.")
: _("No branch named '%s'."),
branch_name);
@ -879,7 +924,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
}
if (!ref_exists(branch->refname)) {
if (!argc || !strcmp(head, branch->name))
if (!argc || branch_checked_out(branch->refname))
die(_("No commit on branch '%s' yet."), branch->name);
die(_("branch '%s' does not exist"), branch->name);
}