refs: unify parse_worktree_ref() and ref_type()

The logic to handle worktree refs (worktrees/NAME/REF and main-worktree/REF) existed in two places: * ref_type() in refs.c * parse_worktree_ref() in worktree.c Collapse this logic together in one function parse_worktree_ref(): this avoids having to cross-check the result of parse_worktree_ref() and ref_type(). Introduce enum ref_worktree_type, which is slightly different from enum ref_type. The latter is a misleading name (one would think that 'ref_type' would have the symref option). Instead, enum ref_worktree_type only makes explicit how a refname relates to a worktree. From this point of view, HEAD and refs/bisect/abc are the same: they specify the current worktree implicitly. The files-backend must avoid packing refs/bisect/* and friends into packed-refs, so expose is_per_worktree_ref() separately. Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Han-Wen Nienhuys committed Sep 19, 2022 at 16:34 UTC 71e5473493612f74244e2fa7a257a868df98be53
8 files changed +126 -150
builtin/reflog.c
+2 -1
@@ -67,7 +67,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
67 * Avoid collecting the same shared ref multiple times because
68 * they are available via all worktrees.
69 */
70 - if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
70 + if (!worktree->is_current &&
71 + parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
72 return 0;
73
74 strbuf_worktree_ref(worktree, &newref, ref);
reflog.c
+3 -10
@@ -312,16 +312,9 @@ static int push_tip_to_list(const char *refname UNUSED,
312
313 static int is_head(const char *refname)
314 {
315 - switch (ref_type(refname)) {
316 - case REF_TYPE_OTHER_PSEUDOREF:
317 - case REF_TYPE_MAIN_PSEUDOREF:
318 - if (parse_worktree_ref(refname, NULL, NULL, &refname))
319 - BUG("not a worktree ref: %s", refname);
320 - break;
321 - default:
322 - break;
323 - }
324 - return !strcmp(refname, "HEAD");
315 + const char *stripped_refname;
316 + parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
317 + return !strcmp(stripped_refname, "HEAD");
318 }
319
320 void reflog_expiry_prepare(const char *refname,
refs.c
+51 -25
@@ -811,7 +811,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
811 return repo_dwim_log(the_repository, str, len, oid, log);
812 }
813
814 -static int is_per_worktree_ref(const char *refname)
814 +int is_per_worktree_ref(const char *refname)
815 {
816 return starts_with(refname, "refs/worktree/") ||
817 starts_with(refname, "refs/bisect/") ||
@@ -827,37 +827,63 @@ static int is_pseudoref_syntax(const char *refname)
827 return 0;
828 }
829
830 + /*
831 + * HEAD is not a pseudoref, but it certainly uses the
832 + * pseudoref syntax.
833 + */
834 return 1;
835 }
836
833 -static int is_main_pseudoref_syntax(const char *refname)
834 -{
835 - return skip_prefix(refname, "main-worktree/", &refname) &&
836 - *refname &&
837 - is_pseudoref_syntax(refname);
837 +static int is_current_worktree_ref(const char *ref) {
838 + return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
839 }
840
840 -static int is_other_pseudoref_syntax(const char *refname)
841 +enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
842 + const char **worktree_name, int *worktree_name_length,
843 + const char **bare_refname)
844 {
842 - if (!skip_prefix(refname, "worktrees/", &refname))
843 - return 0;
844 - refname = strchr(refname, '/');
845 - if (!refname || !refname[1])
846 - return 0;
847 - return is_pseudoref_syntax(refname + 1);
848 -}
845 + const char *name_dummy;
846 + int name_length_dummy;
847 + const char *ref_dummy;
848
850 -enum ref_type ref_type(const char *refname)
851 -{
852 - if (is_per_worktree_ref(refname))
853 - return REF_TYPE_PER_WORKTREE;
854 - if (is_pseudoref_syntax(refname))
855 - return REF_TYPE_PSEUDOREF;
856 - if (is_main_pseudoref_syntax(refname))
857 - return REF_TYPE_MAIN_PSEUDOREF;
858 - if (is_other_pseudoref_syntax(refname))
859 - return REF_TYPE_OTHER_PSEUDOREF;
860 - return REF_TYPE_NORMAL;
849 + if (!worktree_name)
850 + worktree_name = &name_dummy;
851 + if (!worktree_name_length)
852 + worktree_name_length = &name_length_dummy;
853 + if (!bare_refname)
854 + bare_refname = &ref_dummy;
855 +
856 + if (skip_prefix(maybe_worktree_ref, "worktrees/", bare_refname)) {
857 + const char *slash = strchr(*bare_refname, '/');
858 +
859 + *worktree_name = *bare_refname;
860 + if (!slash) {
861 + *worktree_name_length = strlen(*worktree_name);
862 +
863 + /* This is an error condition, and the caller tell because the bare_refname is "" */
864 + *bare_refname = *worktree_name + *worktree_name_length;
865 + return REF_WORKTREE_OTHER;
866 + }
867 +
868 + *worktree_name_length = slash - *bare_refname;
869 + *bare_refname = slash + 1;
870 +
871 + if (is_current_worktree_ref(*bare_refname))
872 + return REF_WORKTREE_OTHER;
873 + }
874 +
875 + *worktree_name = NULL;
876 + *worktree_name_length = 0;
877 +
878 + if (skip_prefix(maybe_worktree_ref, "main-worktree/", bare_refname)
879 + && is_current_worktree_ref(*bare_refname))
880 + return REF_WORKTREE_MAIN;
881 +
882 + *bare_refname = maybe_worktree_ref;
883 + if (is_current_worktree_ref(maybe_worktree_ref))
884 + return REF_WORKTREE_CURRENT;
885 +
886 + return REF_WORKTREE_SHARED;
887 }
888
889 long get_files_ref_lock_timeout_ms(void)
refs.h
+26 -7
@@ -820,15 +820,34 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);
820 */
821 int ref_is_hidden(const char *, const char *);
822
823 -enum ref_type {
824 - REF_TYPE_PER_WORKTREE, /* refs inside refs/ but not shared */
825 - REF_TYPE_PSEUDOREF, /* refs outside refs/ in current worktree */
826 - REF_TYPE_MAIN_PSEUDOREF, /* pseudo refs from the main worktree */
827 - REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees */
828 - REF_TYPE_NORMAL, /* normal/shared refs inside refs/ */
823 +/* Is this a per-worktree ref living in the refs/ namespace? */
824 +int is_per_worktree_ref(const char *refname);
825 +
826 +/* Describes how a refname relates to worktrees */
827 +enum ref_worktree_type {
828 + REF_WORKTREE_CURRENT, /* implicitly per worktree, eg. HEAD or
829 + refs/bisect/SOMETHING */
830 + REF_WORKTREE_MAIN, /* explicitly in main worktree, eg.
831 + main-worktree/HEAD */
832 + REF_WORKTREE_OTHER, /* explicitly in named worktree, eg.
833 + worktrees/bla/HEAD */
834 + REF_WORKTREE_SHARED, /* the default, eg. refs/heads/main */
835 };
836
831 -enum ref_type ref_type(const char *refname);
837 +/*
838 + * Parse a `maybe_worktree_ref` as a ref that possibly refers to a worktree ref
839 + * (ie. either REFNAME, main-worktree/REFNAME or worktree/WORKTREE/REFNAME). It
840 + * returns what kind of ref was found, and in case of REF_WORKTREE_OTHER, the
841 + * worktree name is returned in `worktree_name` (pointing into
842 + * `maybe_worktree_ref`) and `worktree_name_length`. The bare refname (the
843 + * refname stripped of prefixes) is returned in `bare_refname`. The
844 + * `worktree_name`, `worktree_name_length` and `bare_refname` arguments may be
845 + * NULL.
846 + */
847 +enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
848 + const char **worktree_name,
849 + int *worktree_name_length,
850 + const char **bare_refname);
851
852 enum expire_reflog_flags {
853 EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
refs/files-backend.c
+36 -44
@@ -138,44 +138,30 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
138 return refs;
139 }
140
141 -static void files_reflog_path_other_worktrees(struct files_ref_store *refs,
142 - struct strbuf *sb,
143 - const char *refname)
144 -{
145 - const char *real_ref;
146 - const char *worktree_name;
147 - int length;
148 -
149 - if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))
150 - BUG("refname %s is not a other-worktree ref", refname);
151 -
152 - if (worktree_name)
153 - strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
154 - length, worktree_name, real_ref);
155 - else
156 - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir,
157 - real_ref);
158 -}
159 -
141 static void files_reflog_path(struct files_ref_store *refs,
142 struct strbuf *sb,
143 const char *refname)
144 {
164 - switch (ref_type(refname)) {
165 - case REF_TYPE_PER_WORKTREE:
166 - case REF_TYPE_PSEUDOREF:
145 + const char *bare_refname;
146 + const char *wtname;
147 + int wtname_len;
148 + enum ref_worktree_type wt_type = parse_worktree_ref(
149 + refname, &wtname, &wtname_len, &bare_refname);
150 +
151 + switch (wt_type) {
152 + case REF_WORKTREE_CURRENT:
153 strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname);
154 break;
169 - case REF_TYPE_OTHER_PSEUDOREF:
170 - case REF_TYPE_MAIN_PSEUDOREF:
171 - files_reflog_path_other_worktrees(refs, sb, refname);
155 + case REF_WORKTREE_SHARED:
156 + case REF_WORKTREE_MAIN:
157 + strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, bare_refname);
158 break;
173 - case REF_TYPE_NORMAL:
174 - strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
159 + case REF_WORKTREE_OTHER:
160 + strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
161 + wtname_len, wtname, bare_refname);
162 break;
163 default:
177 - BUG("unknown ref type %d of ref %s",
178 - ref_type(refname), refname);
164 + BUG("unknown ref type %d of ref %s", wt_type, refname);
165 }
166 }
167
@@ -183,22 +169,25 @@ static void files_ref_path(struct files_ref_store *refs,
169 struct strbuf *sb,
170 const char *refname)
171 {
186 - switch (ref_type(refname)) {
187 - case REF_TYPE_PER_WORKTREE:
188 - case REF_TYPE_PSEUDOREF:
172 + const char *bare_refname;
173 + const char *wtname;
174 + int wtname_len;
175 + enum ref_worktree_type wt_type = parse_worktree_ref(
176 + refname, &wtname, &wtname_len, &bare_refname);
177 + switch (wt_type) {
178 + case REF_WORKTREE_CURRENT:
179 strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname);
180 break;
191 - case REF_TYPE_MAIN_PSEUDOREF:
192 - if (!skip_prefix(refname, "main-worktree/", &refname))
193 - BUG("ref %s is not a main pseudoref", refname);
194 - /* fallthrough */
195 - case REF_TYPE_OTHER_PSEUDOREF:
196 - case REF_TYPE_NORMAL:
197 - strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
181 + case REF_WORKTREE_OTHER:
182 + strbuf_addf(sb, "%s/worktrees/%.*s/%s", refs->gitcommondir,
183 + wtname_len, wtname, bare_refname);
184 + break;
185 + case REF_WORKTREE_SHARED:
186 + case REF_WORKTREE_MAIN:
187 + strbuf_addf(sb, "%s/%s", refs->gitcommondir, bare_refname);
188 break;
189 default:
200 - BUG("unknown ref type %d of ref %s",
201 - ref_type(refname), refname);
190 + BUG("unknown ref type %d of ref %s", wt_type, refname);
191 }
192 }
193
@@ -771,7 +760,8 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
760
761 while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
762 if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
774 - ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
763 + parse_worktree_ref(iter->iter0->refname, NULL, NULL,
764 + NULL) != REF_WORKTREE_CURRENT)
765 continue;
766
767 if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
@@ -1178,7 +1168,8 @@ static int should_pack_ref(const char *refname,
1168 unsigned int pack_flags)
1169 {
1170 /* Do not pack per-worktree refs: */
1181 - if (ref_type(refname) != REF_TYPE_NORMAL)
1171 + if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
1172 + REF_WORKTREE_SHARED)
1173 return 0;
1174
1175 /* Do not pack non-tags unless PACK_REFS_ALL is set: */
@@ -2267,7 +2258,8 @@ static enum iterator_selection reflog_iterator_select(
2258 */
2259 return ITER_SELECT_0;
2260 } else if (iter_common) {
2270 - if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
2261 + if (parse_worktree_ref(iter_common->refname, NULL, NULL,
2262 + NULL) == REF_WORKTREE_SHARED)
2263 return ITER_SELECT_1;
2264
2265 /*
refs/packed-backend.c
+1 -1
@@ -862,7 +862,7 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
862
863 while ((ok = next_record(iter)) == ITER_OK) {
864 if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
865 - ref_type(iter->base.refname) != REF_TYPE_PER_WORKTREE)
865 + !is_per_worktree_ref(iter->base.refname))
866 continue;
867
868 if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
worktree.c
+7 -52
@@ -489,62 +489,17 @@ int submodule_uses_worktrees(const char *path)
489 return ret;
490 }
491
492 -int parse_worktree_ref(const char *worktree_ref, const char **name,
493 - int *name_length, const char **ref)
494 -{
495 - if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
496 - if (!*worktree_ref)
497 - return -1;
498 - if (name)
499 - *name = NULL;
500 - if (name_length)
501 - *name_length = 0;
502 - if (ref)
503 - *ref = worktree_ref;
504 - return 0;
505 - }
506 - if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
507 - const char *slash = strchr(worktree_ref, '/');
508 -
509 - if (!slash || slash == worktree_ref || !slash[1])
510 - return -1;
511 - if (name)
512 - *name = worktree_ref;
513 - if (name_length)
514 - *name_length = slash - worktree_ref;
515 - if (ref)
516 - *ref = slash + 1;
517 - return 0;
518 - }
519 - return -1;
520 -}
521 -
492 void strbuf_worktree_ref(const struct worktree *wt,
493 struct strbuf *sb,
494 const char *refname)
495 {
526 - switch (ref_type(refname)) {
527 - case REF_TYPE_PSEUDOREF:
528 - case REF_TYPE_PER_WORKTREE:
529 - if (wt && !wt->is_current) {
530 - if (is_main_worktree(wt))
531 - strbuf_addstr(sb, "main-worktree/");
532 - else
533 - strbuf_addf(sb, "worktrees/%s/", wt->id);
534 - }
535 - break;
536 -
537 - case REF_TYPE_MAIN_PSEUDOREF:
538 - case REF_TYPE_OTHER_PSEUDOREF:
539 - break;
540 -
541 - case REF_TYPE_NORMAL:
542 - /*
543 - * For shared refs, don't prefix worktrees/ or
544 - * main-worktree/. It's not necessary and
545 - * files-backend.c can't handle it anyway.
546 - */
547 - break;
496 + if (parse_worktree_ref(refname, NULL, NULL, NULL) ==
497 + REF_WORKTREE_CURRENT &&
498 + wt && !wt->is_current) {
499 + if (is_main_worktree(wt))
500 + strbuf_addstr(sb, "main-worktree/");
501 + else
502 + strbuf_addf(sb, "worktrees/%s/", wt->id);
503 }
504 strbuf_addstr(sb, refname);
505 }
worktree.h
-10
@@ -166,16 +166,6 @@ const char *worktree_git_path(const struct worktree *wt,
166 const char *fmt, ...)
167 __attribute__((format (printf, 2, 3)));
168
169 -/*
170 - * Parse a worktree ref (i.e. with prefix main-worktree/ or
171 - * worktrees/) and return the position of the worktree's name and
172 - * length (or NULL and zero if it's main worktree), and ref.
173 - *
174 - * All name, name_length and ref arguments could be NULL.
175 - */
176 -int parse_worktree_ref(const char *worktree_ref, const char **name,
177 - int *name_length, const char **ref);
178 -
169 /*
170 * Return a refname suitable for access from the current ref store.
171 */