Skip to content

Commit 352d72a

Browse files
committed
Merge branch 'nd/worktree-various-heads'
The experimental "multiple worktree" feature gains more safety to forbid operations on a branch that is checked out or being actively worked on elsewhere, by noticing that e.g. it is being rebased. * nd/worktree-various-heads: branch: do not rename a branch under bisect or rebase worktree.c: check whether branch is bisected in another worktree wt-status.c: split bisect detection out of wt_status_get_state() worktree.c: check whether branch is rebased in another worktree worktree.c: avoid referencing to worktrees[i] multiple times wt-status.c: make wt_status_check_rebase() work on any worktree wt-status.c: split rebase detection out of wt_status_get_state() path.c: refactor and add worktree_git_path() worktree.c: mark current worktree worktree.c: make find_shared_symref() return struct worktree * worktree.c: store "id" instead of "git_dir" path.c: add git_common_path() and strbuf_git_common_path() dir.c: rename str(n)cmp_icase to fspath(n)cmp
2 parents 9ce2824 + 14ace5b commit 352d72a

17 files changed

+344
-82
lines changed

branch.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,16 @@ void remove_branch_state(void)
334334
unlink(git_path_squash_msg());
335335
}
336336

337-
void die_if_checked_out(const char *branch)
337+
void die_if_checked_out(const char *branch, int ignore_current_worktree)
338338
{
339-
char *existing;
339+
const struct worktree *wt;
340340

341-
existing = find_shared_symref("HEAD", branch);
342-
if (existing) {
343-
skip_prefix(branch, "refs/heads/", &branch);
344-
die(_("'%s' is already checked out at '%s'"), branch, existing);
345-
}
341+
wt = find_shared_symref("HEAD", branch);
342+
if (!wt || (ignore_current_worktree && wt->is_current))
343+
return;
344+
skip_prefix(branch, "refs/heads/", &branch);
345+
die(_("'%s' is already checked out at '%s'"),
346+
branch, wt->path);
346347
}
347348

348349
int replace_each_worktree_head_symref(const char *oldref, const char *newref)
@@ -357,7 +358,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
357358
if (strcmp(oldref, worktrees[i]->head_ref))
358359
continue;
359360

360-
if (set_worktree_head_symref(worktrees[i]->git_dir, newref)) {
361+
if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
362+
newref)) {
361363
ret = -1;
362364
error(_("HEAD of working tree %s is not updated"),
363365
worktrees[i]->path);

branch.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
5858
* worktree and die (with a message describing its checkout location) if
5959
* it is.
6060
*/
61-
extern void die_if_checked_out(const char *branch);
61+
extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
6262

6363
/*
6464
* Update all per-worktree HEADs pointing at the old ref to point the new ref.

builtin/branch.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,12 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
220220
name = mkpathdup(fmt, bname.buf);
221221

222222
if (kinds == FILTER_REFS_BRANCHES) {
223-
char *worktree = find_shared_symref("HEAD", name);
224-
if (worktree) {
223+
const struct worktree *wt =
224+
find_shared_symref("HEAD", name);
225+
if (wt) {
225226
error(_("Cannot delete branch '%s' "
226227
"checked out at '%s'"),
227-
bname.buf, worktree);
228-
free(worktree);
228+
bname.buf, wt->path);
229229
ret = 1;
230230
continue;
231231
}
@@ -526,6 +526,29 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
526526
ref_array_clear(&array);
527527
}
528528

529+
static void reject_rebase_or_bisect_branch(const char *target)
530+
{
531+
struct worktree **worktrees = get_worktrees();
532+
int i;
533+
534+
for (i = 0; worktrees[i]; i++) {
535+
struct worktree *wt = worktrees[i];
536+
537+
if (!wt->is_detached)
538+
continue;
539+
540+
if (is_worktree_being_rebased(wt, target))
541+
die(_("Branch %s is being rebased at %s"),
542+
target, wt->path);
543+
544+
if (is_worktree_being_bisected(wt, target))
545+
die(_("Branch %s is being bisected at %s"),
546+
target, wt->path);
547+
}
548+
549+
free_worktrees(worktrees);
550+
}
551+
529552
static void rename_branch(const char *oldname, const char *newname, int force)
530553
{
531554
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
@@ -555,6 +578,8 @@ static void rename_branch(const char *oldname, const char *newname, int force)
555578

556579
validate_new_branchname(newname, &newref, force, clobber_head_ok);
557580

581+
reject_rebase_or_bisect_branch(oldref.buf);
582+
558583
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
559584
oldref.buf, newref.buf);
560585

builtin/checkout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ static int checkout_branch(struct checkout_opts *opts,
11101110
char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
11111111
if (head_ref &&
11121112
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
1113-
die_if_checked_out(new->path);
1113+
die_if_checked_out(new->path, 1);
11141114
free(head_ref);
11151115
}
11161116

builtin/notes.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -847,15 +847,15 @@ static int merge(int argc, const char **argv, const char *prefix)
847847
update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
848848
0, UPDATE_REFS_DIE_ON_ERR);
849849
else { /* Merge has unresolved conflicts */
850-
char *existing;
850+
const struct worktree *wt;
851851
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
852852
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
853853
0, UPDATE_REFS_DIE_ON_ERR);
854854
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
855-
existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
856-
if (existing)
855+
wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
856+
if (wt)
857857
die(_("A notes merge into %s is already in-progress at %s"),
858-
default_notes_ref(), existing);
858+
default_notes_ref(), wt->path);
859859
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
860860
die("Failed to store link to current notes ref (%s)",
861861
default_notes_ref());

builtin/worktree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ static int add_worktree(const char *path, const char *refname,
205205
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
206206
ref_exists(symref.buf)) { /* it's a branch */
207207
if (!opts->force)
208-
die_if_checked_out(symref.buf);
208+
die_if_checked_out(symref.buf, 0);
209209
} else { /* must be a commit */
210210
commit = lookup_commit_reference_by_name(refname);
211211
if (!commit)
@@ -349,7 +349,7 @@ static int add(int ac, const char **av, const char *prefix)
349349
if (!opts.force &&
350350
!strbuf_check_branch_ref(&symref, opts.new_branch) &&
351351
ref_exists(symref.buf))
352-
die_if_checked_out(symref.buf);
352+
die_if_checked_out(symref.buf, 0);
353353
strbuf_release(&symref);
354354
}
355355

cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,11 +808,14 @@ extern void check_repository_format(void);
808808
*/
809809
extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
810810
extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
811+
extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
811812

812813
extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
813814
__attribute__((format (printf, 3, 4)));
814815
extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
815816
__attribute__((format (printf, 2, 3)));
817+
extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
818+
__attribute__((format (printf, 2, 3)));
816819
extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
817820
__attribute__((format (printf, 2, 3)));
818821
extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,

dir.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
5353
int check_only, const struct path_simplify *simplify);
5454
static int get_dtype(struct dirent *de, const char *path, int len);
5555

56-
/* helper string functions with support for the ignore_case flag */
57-
int strcmp_icase(const char *a, const char *b)
56+
int fspathcmp(const char *a, const char *b)
5857
{
5958
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
6059
}
6160

62-
int strncmp_icase(const char *a, const char *b, size_t count)
61+
int fspathncmp(const char *a, const char *b, size_t count)
6362
{
6463
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
6564
}
@@ -795,12 +794,12 @@ int match_basename(const char *basename, int basenamelen,
795794
{
796795
if (prefix == patternlen) {
797796
if (patternlen == basenamelen &&
798-
!strncmp_icase(pattern, basename, basenamelen))
797+
!fspathncmp(pattern, basename, basenamelen))
799798
return 1;
800799
} else if (flags & EXC_FLAG_ENDSWITH) {
801800
/* "*literal" matching against "fooliteral" */
802801
if (patternlen - 1 <= basenamelen &&
803-
!strncmp_icase(pattern + 1,
802+
!fspathncmp(pattern + 1,
804803
basename + basenamelen - (patternlen - 1),
805804
patternlen - 1))
806805
return 1;
@@ -837,7 +836,7 @@ int match_pathname(const char *pathname, int pathlen,
837836
*/
838837
if (pathlen < baselen + 1 ||
839838
(baselen && pathname[baselen] != '/') ||
840-
strncmp_icase(pathname, base, baselen))
839+
fspathncmp(pathname, base, baselen))
841840
return 0;
842841

843842
namelen = baselen ? pathlen - baselen - 1 : pathlen;
@@ -851,7 +850,7 @@ int match_pathname(const char *pathname, int pathlen,
851850
if (prefix > namelen)
852851
return 0;
853852

854-
if (strncmp_icase(pattern, name, prefix))
853+
if (fspathncmp(pattern, name, prefix))
855854
return 0;
856855
pattern += prefix;
857856
patternlen -= prefix;

dir.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
270270
/* tries to remove the path with empty directories along it, ignores ENOENT */
271271
extern int remove_path(const char *path);
272272

273-
extern int strcmp_icase(const char *a, const char *b);
274-
extern int strncmp_icase(const char *a, const char *b, size_t count);
273+
extern int fspathcmp(const char *a, const char *b);
274+
extern int fspathncmp(const char *a, const char *b, size_t count);
275275

276276
/*
277277
* The prefix part of pattern must not contains wildcards.

fast-import.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ static int tree_content_set(
15121512
t = root->tree;
15131513
for (i = 0; i < t->entry_count; i++) {
15141514
e = t->entries[i];
1515-
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
1515+
if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
15161516
if (!*slash1) {
15171517
if (!S_ISDIR(mode)
15181518
&& e->versions[1].mode == mode
@@ -1602,7 +1602,7 @@ static int tree_content_remove(
16021602
t = root->tree;
16031603
for (i = 0; i < t->entry_count; i++) {
16041604
e = t->entries[i];
1605-
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
1605+
if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
16061606
if (*slash1 && !S_ISDIR(e->versions[1].mode))
16071607
/*
16081608
* If p names a file in some subdirectory, and a
@@ -1669,7 +1669,7 @@ static int tree_content_get(
16691669
t = root->tree;
16701670
for (i = 0; i < t->entry_count; i++) {
16711671
e = t->entries[i];
1672-
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
1672+
if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
16731673
if (!*slash1)
16741674
goto found_entry;
16751675
if (!S_ISDIR(e->versions[1].mode))

0 commit comments

Comments
 (0)