зеркало из https://github.com/microsoft/git.git
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:
Коммит
d3f2e4ab13
27
branch.c
27
branch.c
|
@ -840,30 +840,3 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
|
|||
|
||||
free_worktrees(worktrees);
|
||||
}
|
||||
|
||||
int replace_each_worktree_head_symref(const char *oldref, const char *newref,
|
||||
const char *logmsg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct worktree **worktrees = get_worktrees();
|
||||
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);
|
||||
}
|
||||
|
||||
free_worktrees(worktrees);
|
||||
return ret;
|
||||
}
|
||||
|
|
8
branch.h
8
branch.h
|
@ -155,12 +155,4 @@ int read_branch_desc(struct strbuf *, const char *branch_name);
|
|||
*/
|
||||
void die_if_checked_out(const char *branch, int ignore_current_worktree);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int replace_each_worktree_head_symref(const char *oldref, const char *newref,
|
||||
const char *logmsg);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -239,6 +239,21 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
|
|||
git worktree prune
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -M fails if updating any linked working tree fails' '
|
||||
git worktree add -b baz bazdir1 &&
|
||||
git worktree add -f bazdir2 baz &&
|
||||
touch .git/worktrees/bazdir1/HEAD.lock &&
|
||||
test_must_fail git branch -M baz bam &&
|
||||
test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
|
||||
git branch -M bam baz &&
|
||||
rm .git/worktrees/bazdir1/HEAD.lock &&
|
||||
touch .git/worktrees/bazdir2/HEAD.lock &&
|
||||
test_must_fail git branch -M baz bam &&
|
||||
test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
|
||||
rm -rf bazdir1 bazdir2 &&
|
||||
git worktree prune
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
|
||||
git checkout -b baz &&
|
||||
git worktree add -f bazdir baz &&
|
||||
|
@ -283,6 +298,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 &&
|
||||
|
|
|
@ -221,4 +221,22 @@ test_expect_success 'fatal descriptions on non-existent branch' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'error descriptions on orphan branch' '
|
||||
test_when_finished git worktree remove -f wt &&
|
||||
git worktree add wt --detach &&
|
||||
git -C wt checkout --orphan orphan-branch &&
|
||||
test_branch_op_in_wt() {
|
||||
test_orphan_error() {
|
||||
test_must_fail git $* 2>actual &&
|
||||
test_i18ngrep "No commit on branch .orphan-branch. yet.$" actual
|
||||
} &&
|
||||
test_orphan_error -C wt branch $1 $2 && # implicit branch
|
||||
test_orphan_error -C wt branch $1 orphan-branch $2 && # explicit branch
|
||||
test_orphan_error branch $1 orphan-branch $2 # different worktree
|
||||
} &&
|
||||
test_branch_op_in_wt --edit-description &&
|
||||
test_branch_op_in_wt --set-upstream-to=ne &&
|
||||
test_branch_op_in_wt -c new-branch
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Загрузка…
Ссылка в новой задаче