diff --git a/builtin/reflog.c b/builtin/reflog.c index 57c5c0d061..270681dcdf 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -67,7 +67,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid UNUSED, * Avoid collecting the same shared ref multiple times because * they are available via all worktrees. */ - if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL) + if (!worktree->is_current && + parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED) return 0; strbuf_worktree_ref(worktree, &newref, ref); diff --git a/reflog.c b/reflog.c index d258fd3199..78e9350e20 100644 --- a/reflog.c +++ b/reflog.c @@ -312,16 +312,9 @@ static int push_tip_to_list(const char *refname UNUSED, static int is_head(const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_MAIN_PSEUDOREF: - if (parse_worktree_ref(refname, NULL, NULL, &refname)) - BUG("not a worktree ref: %s", refname); - break; - default: - break; - } - return !strcmp(refname, "HEAD"); + const char *stripped_refname; + parse_worktree_ref(refname, NULL, NULL, &stripped_refname); + return !strcmp(stripped_refname, "HEAD"); } void reflog_expiry_prepare(const char *refname, diff --git a/refs.c b/refs.c index c89d558892..1491ae937e 100644 --- a/refs.c +++ b/refs.c @@ -811,7 +811,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log) return repo_dwim_log(the_repository, str, len, oid, log); } -static int is_per_worktree_ref(const char *refname) +int is_per_worktree_ref(const char *refname) { return starts_with(refname, "refs/worktree/") || starts_with(refname, "refs/bisect/") || @@ -827,37 +827,63 @@ static int is_pseudoref_syntax(const char *refname) return 0; } + /* + * HEAD is not a pseudoref, but it certainly uses the + * pseudoref syntax. + */ return 1; } -static int is_main_pseudoref_syntax(const char *refname) -{ - return skip_prefix(refname, "main-worktree/", &refname) && - *refname && - is_pseudoref_syntax(refname); +static int is_current_worktree_ref(const char *ref) { + return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref); } -static int is_other_pseudoref_syntax(const char *refname) +enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref, + const char **worktree_name, int *worktree_name_length, + const char **bare_refname) { - if (!skip_prefix(refname, "worktrees/", &refname)) - return 0; - refname = strchr(refname, '/'); - if (!refname || !refname[1]) - return 0; - return is_pseudoref_syntax(refname + 1); -} + const char *name_dummy; + int name_length_dummy; + const char *ref_dummy; -enum ref_type ref_type(const char *refname) -{ - if (is_per_worktree_ref(refname)) - return REF_TYPE_PER_WORKTREE; - if (is_pseudoref_syntax(refname)) - return REF_TYPE_PSEUDOREF; - if (is_main_pseudoref_syntax(refname)) - return REF_TYPE_MAIN_PSEUDOREF; - if (is_other_pseudoref_syntax(refname)) - return REF_TYPE_OTHER_PSEUDOREF; - return REF_TYPE_NORMAL; + if (!worktree_name) + worktree_name = &name_dummy; + if (!worktree_name_length) + worktree_name_length = &name_length_dummy; + if (!bare_refname) + bare_refname = &ref_dummy; + + if (skip_prefix(maybe_worktree_ref, "worktrees/", bare_refname)) { + const char *slash = strchr(*bare_refname, '/'); + + *worktree_name = *bare_refname; + if (!slash) { + *worktree_name_length = strlen(*worktree_name); + + /* This is an error condition, and the caller tell because the bare_refname is "" */ + *bare_refname = *worktree_name + *worktree_name_length; + return REF_WORKTREE_OTHER; + } + + *worktree_name_length = slash - *bare_refname; + *bare_refname = slash + 1; + + if (is_current_worktree_ref(*bare_refname)) + return REF_WORKTREE_OTHER; + } + + *worktree_name = NULL; + *worktree_name_length = 0; + + if (skip_prefix(maybe_worktree_ref, "main-worktree/", bare_refname) + && is_current_worktree_ref(*bare_refname)) + return REF_WORKTREE_MAIN; + + *bare_refname = maybe_worktree_ref; + if (is_current_worktree_ref(maybe_worktree_ref)) + return REF_WORKTREE_CURRENT; + + return REF_WORKTREE_SHARED; } long get_files_ref_lock_timeout_ms(void) diff --git a/refs.h b/refs.h index d6575b8c2b..8958717a17 100644 --- a/refs.h +++ b/refs.h @@ -820,15 +820,34 @@ int parse_hide_refs_config(const char *var, const char *value, const char *); */ int ref_is_hidden(const char *, const char *); -enum ref_type { - REF_TYPE_PER_WORKTREE, /* refs inside refs/ but not shared */ - REF_TYPE_PSEUDOREF, /* refs outside refs/ in current worktree */ - REF_TYPE_MAIN_PSEUDOREF, /* pseudo refs from the main worktree */ - REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees */ - REF_TYPE_NORMAL, /* normal/shared refs inside refs/ */ +/* Is this a per-worktree ref living in the refs/ namespace? */ +int is_per_worktree_ref(const char *refname); + +/* Describes how a refname relates to worktrees */ +enum ref_worktree_type { + REF_WORKTREE_CURRENT, /* implicitly per worktree, eg. HEAD or + refs/bisect/SOMETHING */ + REF_WORKTREE_MAIN, /* explicitly in main worktree, eg. + main-worktree/HEAD */ + REF_WORKTREE_OTHER, /* explicitly in named worktree, eg. + worktrees/bla/HEAD */ + REF_WORKTREE_SHARED, /* the default, eg. refs/heads/main */ }; -enum ref_type ref_type(const char *refname); +/* + * Parse a `maybe_worktree_ref` as a ref that possibly refers to a worktree ref + * (ie. either REFNAME, main-worktree/REFNAME or worktree/WORKTREE/REFNAME). It + * returns what kind of ref was found, and in case of REF_WORKTREE_OTHER, the + * worktree name is returned in `worktree_name` (pointing into + * `maybe_worktree_ref`) and `worktree_name_length`. The bare refname (the + * refname stripped of prefixes) is returned in `bare_refname`. The + * `worktree_name`, `worktree_name_length` and `bare_refname` arguments may be + * NULL. + */ +enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref, + const char **worktree_name, + int *worktree_name_length, + const char **bare_refname); enum expire_reflog_flags { EXPIRE_REFLOGS_DRY_RUN = 1 << 0, diff --git a/refs/files-backend.c b/refs/files-backend.c index e4009b3c42..b89954355d 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -138,44 +138,30 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store, return refs; } -static void files_reflog_path_other_worktrees(struct files_ref_store *refs, - struct strbuf *sb, - const char *refname) -{ - const char *real_ref; - const char *worktree_name; - int length; - - if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref)) - BUG("refname %s is not a other-worktree ref", refname); - - if (worktree_name) - strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir, - length, worktree_name, real_ref); - else - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, - real_ref); -} - static void files_reflog_path(struct files_ref_store *refs, struct strbuf *sb, const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_PER_WORKTREE: - case REF_TYPE_PSEUDOREF: + const char *bare_refname; + const char *wtname; + int wtname_len; + enum ref_worktree_type wt_type = parse_worktree_ref( + refname, &wtname, &wtname_len, &bare_refname); + + switch (wt_type) { + case REF_WORKTREE_CURRENT: strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname); break; - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_MAIN_PSEUDOREF: - files_reflog_path_other_worktrees(refs, sb, refname); + case REF_WORKTREE_SHARED: + case REF_WORKTREE_MAIN: + strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, bare_refname); break; - case REF_TYPE_NORMAL: - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname); + case REF_WORKTREE_OTHER: + strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir, + wtname_len, wtname, bare_refname); break; default: - BUG("unknown ref type %d of ref %s", - ref_type(refname), refname); + BUG("unknown ref type %d of ref %s", wt_type, refname); } } @@ -183,22 +169,25 @@ static void files_ref_path(struct files_ref_store *refs, struct strbuf *sb, const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_PER_WORKTREE: - case REF_TYPE_PSEUDOREF: + const char *bare_refname; + const char *wtname; + int wtname_len; + enum ref_worktree_type wt_type = parse_worktree_ref( + refname, &wtname, &wtname_len, &bare_refname); + switch (wt_type) { + case REF_WORKTREE_CURRENT: strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname); break; - case REF_TYPE_MAIN_PSEUDOREF: - if (!skip_prefix(refname, "main-worktree/", &refname)) - BUG("ref %s is not a main pseudoref", refname); - /* fallthrough */ - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_NORMAL: - strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname); + case REF_WORKTREE_OTHER: + strbuf_addf(sb, "%s/worktrees/%.*s/%s", refs->gitcommondir, + wtname_len, wtname, bare_refname); + break; + case REF_WORKTREE_SHARED: + case REF_WORKTREE_MAIN: + strbuf_addf(sb, "%s/%s", refs->gitcommondir, bare_refname); break; default: - BUG("unknown ref type %d of ref %s", - ref_type(refname), refname); + BUG("unknown ref type %d of ref %s", wt_type, refname); } } @@ -771,7 +760,8 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) { if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY && - ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE) + parse_worktree_ref(iter->iter0->refname, NULL, NULL, + NULL) != REF_WORKTREE_CURRENT) continue; if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) && @@ -1178,7 +1168,8 @@ static int should_pack_ref(const char *refname, unsigned int pack_flags) { /* Do not pack per-worktree refs: */ - if (ref_type(refname) != REF_TYPE_NORMAL) + if (parse_worktree_ref(refname, NULL, NULL, NULL) != + REF_WORKTREE_SHARED) return 0; /* Do not pack non-tags unless PACK_REFS_ALL is set: */ @@ -2267,7 +2258,8 @@ static enum iterator_selection reflog_iterator_select( */ return ITER_SELECT_0; } else if (iter_common) { - if (ref_type(iter_common->refname) == REF_TYPE_NORMAL) + if (parse_worktree_ref(iter_common->refname, NULL, NULL, + NULL) == REF_WORKTREE_SHARED) return ITER_SELECT_1; /* diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 43cdb97f8b..c1c71d183e 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -862,7 +862,7 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator) while ((ok = next_record(iter)) == ITER_OK) { if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY && - ref_type(iter->base.refname) != REF_TYPE_PER_WORKTREE) + !is_per_worktree_ref(iter->base.refname)) continue; if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && diff --git a/worktree.c b/worktree.c index 257ba4cf1e..aa43c64119 100644 --- a/worktree.c +++ b/worktree.c @@ -489,62 +489,17 @@ int submodule_uses_worktrees(const char *path) return ret; } -int parse_worktree_ref(const char *worktree_ref, const char **name, - int *name_length, const char **ref) -{ - if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) { - if (!*worktree_ref) - return -1; - if (name) - *name = NULL; - if (name_length) - *name_length = 0; - if (ref) - *ref = worktree_ref; - return 0; - } - if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) { - const char *slash = strchr(worktree_ref, '/'); - - if (!slash || slash == worktree_ref || !slash[1]) - return -1; - if (name) - *name = worktree_ref; - if (name_length) - *name_length = slash - worktree_ref; - if (ref) - *ref = slash + 1; - return 0; - } - return -1; -} - void strbuf_worktree_ref(const struct worktree *wt, struct strbuf *sb, const char *refname) { - switch (ref_type(refname)) { - case REF_TYPE_PSEUDOREF: - case REF_TYPE_PER_WORKTREE: - if (wt && !wt->is_current) { - if (is_main_worktree(wt)) - strbuf_addstr(sb, "main-worktree/"); - else - strbuf_addf(sb, "worktrees/%s/", wt->id); - } - break; - - case REF_TYPE_MAIN_PSEUDOREF: - case REF_TYPE_OTHER_PSEUDOREF: - break; - - case REF_TYPE_NORMAL: - /* - * For shared refs, don't prefix worktrees/ or - * main-worktree/. It's not necessary and - * files-backend.c can't handle it anyway. - */ - break; + if (parse_worktree_ref(refname, NULL, NULL, NULL) == + REF_WORKTREE_CURRENT && + wt && !wt->is_current) { + if (is_main_worktree(wt)) + strbuf_addstr(sb, "main-worktree/"); + else + strbuf_addf(sb, "worktrees/%s/", wt->id); } strbuf_addstr(sb, refname); } diff --git a/worktree.h b/worktree.h index e9e839926b..9dcea6fc8c 100644 --- a/worktree.h +++ b/worktree.h @@ -166,16 +166,6 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...) __attribute__((format (printf, 2, 3))); -/* - * Parse a worktree ref (i.e. with prefix main-worktree/ or - * worktrees/) and return the position of the worktree's name and - * length (or NULL and zero if it's main worktree), and ref. - * - * All name, name_length and ref arguments could be NULL. - */ -int parse_worktree_ref(const char *worktree_ref, const char **name, - int *name_length, const char **ref); - /* * Return a refname suitable for access from the current ref store. */