refs/files: sort merged worktree and common reflogs

When iterating through reflogs in a worktree we create a merged iterator that merges reflogs from both refdbs. The resulting refs are ordered so that instead we first return all worktree reflogs before we return all common refs. This is the only remaining case where a ref iterator returns entries in a non-lexicographic order. The result would look something like the following (listed with a command we introduce in a subsequent commit): ``` $ git reflog list HEAD refs/worktree/per-worktree refs/heads/main refs/heads/wt ``` So we first print the per-worktree reflogs in lexicographic order, then the common reflogs in lexicographic order. This is confusing and not consistent with how we print per-worktree refs, which are exclusively sorted lexicographically. Sort reflogs lexicographically in the same way as we sort normal refs. As this is already implemented properly by the "reftable" backend via a separate selection function, we simply pull out that logic and reuse it for the "files" backend. As logs are properly sorted now, mark the merged reflog iterator as sorted. Tests will be added in a subsequent commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Feb 21, 2024 at 13:37 UTC 6f227800176d7ed1d1c50d64ef5d5e485f5fbee3
4 files changed +56 -73
refs/files-backend.c
+2 -28
@@ -2210,32 +2210,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
2210 return ref_iterator;
2211 }
2212
2213 -static enum iterator_selection reflog_iterator_select(
2214 - struct ref_iterator *iter_worktree,
2215 - struct ref_iterator *iter_common,
2216 - void *cb_data UNUSED)
2217 -{
2218 - if (iter_worktree) {
2219 - /*
2220 - * We're a bit loose here. We probably should ignore
2221 - * common refs if they are accidentally added as
2222 - * per-worktree refs.
2223 - */
2224 - return ITER_SELECT_0;
2225 - } else if (iter_common) {
2226 - if (parse_worktree_ref(iter_common->refname, NULL, NULL,
2227 - NULL) == REF_WORKTREE_SHARED)
2228 - return ITER_SELECT_1;
2229 -
2230 - /*
2231 - * The main ref store may contain main worktree's
2232 - * per-worktree refs, which should be ignored
2233 - */
2234 - return ITER_SKIP_1;
2235 - } else
2236 - return ITER_DONE;
2237 -}
2238 -
2213 static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
2214 {
2215 struct files_ref_store *refs =
@@ -2246,9 +2220,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
2220 return reflog_iterator_begin(ref_store, refs->gitcommondir);
2221 } else {
2222 return merge_ref_iterator_begin(
2249 - 0, reflog_iterator_begin(ref_store, refs->base.gitdir),
2223 + 1, reflog_iterator_begin(ref_store, refs->base.gitdir),
2224 reflog_iterator_begin(ref_store, refs->gitcommondir),
2251 - reflog_iterator_select, refs);
2225 + ref_iterator_select, refs);
2226 }
2227 }
2228
refs/iterator.c
+43
@@ -98,6 +98,49 @@ struct merge_ref_iterator {
98 struct ref_iterator **current;
99 };
100
101 +enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
102 + struct ref_iterator *iter_common,
103 + void *cb_data UNUSED)
104 +{
105 + if (iter_worktree && !iter_common) {
106 + /*
107 + * Return the worktree ref if there are no more common refs.
108 + */
109 + return ITER_SELECT_0;
110 + } else if (iter_common) {
111 + /*
112 + * In case we have pending worktree and common refs we need to
113 + * yield them based on their lexicographical order. Worktree
114 + * refs that have the same name as common refs shadow the
115 + * latter.
116 + */
117 + if (iter_worktree) {
118 + int cmp = strcmp(iter_worktree->refname,
119 + iter_common->refname);
120 + if (cmp < 0)
121 + return ITER_SELECT_0;
122 + else if (!cmp)
123 + return ITER_SELECT_0_SKIP_1;
124 + }
125 +
126 + /*
127 + * We now know that the lexicographically-next ref is a common
128 + * ref. When the common ref is a shared one we return it.
129 + */
130 + if (parse_worktree_ref(iter_common->refname, NULL, NULL,
131 + NULL) == REF_WORKTREE_SHARED)
132 + return ITER_SELECT_1;
133 +
134 + /*
135 + * Otherwise, if the common ref is a per-worktree ref we skip
136 + * it because it would belong to the main worktree, not ours.
137 + */
138 + return ITER_SKIP_1;
139 + } else {
140 + return ITER_DONE;
141 + }
142 +}
143 +
144 static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
145 {
146 struct merge_ref_iterator *iter =
refs/refs-internal.h
+9
@@ -386,6 +386,15 @@ typedef enum iterator_selection ref_iterator_select_fn(
386 struct ref_iterator *iter0, struct ref_iterator *iter1,
387 void *cb_data);
388
389 +/*
390 + * An implementation of ref_iterator_select_fn that merges worktree and common
391 + * refs. Per-worktree refs from the common iterator are ignored, worktree refs
392 + * override common refs. Refs are selected lexicographically.
393 + */
394 +enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
395 + struct ref_iterator *iter_common,
396 + void *cb_data);
397 +
398 /*
399 * Iterate over the entries from iter0 and iter1, with the values
400 * interleaved as directed by the select function. The iterator takes
refs/reftable-backend.c
+2 -45
@@ -504,49 +504,6 @@ done:
504 return iter;
505 }
506
507 -static enum iterator_selection iterator_select(struct ref_iterator *iter_worktree,
508 - struct ref_iterator *iter_common,
509 - void *cb_data UNUSED)
510 -{
511 - if (iter_worktree && !iter_common) {
512 - /*
513 - * Return the worktree ref if there are no more common refs.
514 - */
515 - return ITER_SELECT_0;
516 - } else if (iter_common) {
517 - /*
518 - * In case we have pending worktree and common refs we need to
519 - * yield them based on their lexicographical order. Worktree
520 - * refs that have the same name as common refs shadow the
521 - * latter.
522 - */
523 - if (iter_worktree) {
524 - int cmp = strcmp(iter_worktree->refname,
525 - iter_common->refname);
526 - if (cmp < 0)
527 - return ITER_SELECT_0;
528 - else if (!cmp)
529 - return ITER_SELECT_0_SKIP_1;
530 - }
531 -
532 - /*
533 - * We now know that the lexicographically-next ref is a common
534 - * ref. When the common ref is a shared one we return it.
535 - */
536 - if (parse_worktree_ref(iter_common->refname, NULL, NULL,
537 - NULL) == REF_WORKTREE_SHARED)
538 - return ITER_SELECT_1;
539 -
540 - /*
541 - * Otherwise, if the common ref is a per-worktree ref we skip
542 - * it because it would belong to the main worktree, not ours.
543 - */
544 - return ITER_SKIP_1;
545 - } else {
546 - return ITER_DONE;
547 - }
548 -}
549 -
507 static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
508 const char *prefix,
509 const char **exclude_patterns,
@@ -576,7 +533,7 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
533 */
534 worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
535 return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
579 - iterator_select, NULL);
536 + ref_iterator_select, NULL);
537 }
538
539 static int reftable_be_read_raw_ref(struct ref_store *ref_store,
@@ -1759,7 +1716,7 @@ static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store *
1716 worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
1717
1718 return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base,
1762 - iterator_select, NULL);
1719 + ref_iterator_select, NULL);
1720 }
1721
1722 static int yield_log_record(struct reftable_log_record *log,