Skip to content

Commit 8d9f536

Browse files
committed
Merge branch 'kn/for-each-ref-skip'
"git for-each-ref" learns "--start-after" option to help applications that want to page its output. * kn/for-each-ref-skip: ref-cache: set prefix_state when seeking for-each-ref: introduce a '--start-after' option ref-filter: remove unnecessary else clause refs: selectively set prefix in the seek functions ref-cache: remove unused function 'find_ref_entry()' refs: expose `ref_iterator` via 'refs.h'
2 parents 866e6a3 + 9201261 commit 8d9f536

15 files changed

+584
-243
lines changed

Documentation/git-for-each-ref.adoc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ SYNOPSIS
1414
[--points-at=<object>]
1515
[--merged[=<object>]] [--no-merged[=<object>]]
1616
[--contains[=<object>]] [--no-contains[=<object>]]
17-
[--exclude=<pattern> ...]
17+
[--exclude=<pattern> ...] [--start-after=<marker>]
1818

1919
DESCRIPTION
2020
-----------
@@ -108,6 +108,14 @@ TAB %(refname)`.
108108
--include-root-refs::
109109
List root refs (HEAD and pseudorefs) apart from regular refs.
110110

111+
--start-after=<marker>::
112+
Allows paginating the output by skipping references up to and including the
113+
specified marker. When paging, it should be noted that references may be
114+
deleted, modified or added between invocations. Output will only yield those
115+
references which follow the marker lexicographically. Output begins from the
116+
first reference that would come after the marker alphabetically. Cannot be
117+
used with general pattern matching or custom sort options.
118+
111119
FIELD NAMES
112120
-----------
113121

builtin/for-each-ref.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ static char const * const for_each_ref_usage[] = {
1313
N_("git for-each-ref [--points-at <object>]"),
1414
N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"),
1515
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
16+
N_("git for-each-ref [--start-after <marker>]"),
1617
NULL
1718
};
1819

@@ -44,6 +45,7 @@ int cmd_for_each_ref(int argc,
4445
OPT_GROUP(""),
4546
OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
4647
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
48+
OPT_STRING( 0 , "start-after", &filter.start_after, N_("start-start"), N_("start iteration after the provided marker")),
4749
OPT__COLOR(&format.use_color, N_("respect format colors")),
4850
OPT_REF_FILTER_EXCLUDE(&filter),
4951
OPT_REF_SORT(&sorting_options),
@@ -79,6 +81,9 @@ int cmd_for_each_ref(int argc,
7981
if (verify_ref_format(&format))
8082
usage_with_options(for_each_ref_usage, opts);
8183

84+
if (filter.start_after && sorting_options.nr > 1)
85+
die(_("cannot use --start-after with custom sort options"));
86+
8287
sorting = ref_sorting_options(&sorting_options);
8388
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
8489
filter.ignore_case = icase;
@@ -100,6 +105,9 @@ int cmd_for_each_ref(int argc,
100105
filter.name_patterns = argv;
101106
}
102107

108+
if (filter.start_after && filter.name_patterns && filter.name_patterns[0])
109+
die(_("cannot use --start-after with patterns"));
110+
103111
if (include_root_refs)
104112
flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;
105113

ref-filter.c

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,41 @@ static int filter_exclude_match(struct ref_filter *filter, const char *refname)
26832683
return match_pattern(filter->exclude.v, refname, filter->ignore_case);
26842684
}
26852685

2686+
/*
2687+
* We need to seek to the reference right after a given marker but excluding any
2688+
* matching references. So we seek to the lexicographically next reference.
2689+
*/
2690+
static int start_ref_iterator_after(struct ref_iterator *iter, const char *marker)
2691+
{
2692+
struct strbuf sb = STRBUF_INIT;
2693+
int ret;
2694+
2695+
strbuf_addstr(&sb, marker);
2696+
strbuf_addch(&sb, 1);
2697+
2698+
ret = ref_iterator_seek(iter, sb.buf, 0);
2699+
2700+
strbuf_release(&sb);
2701+
return ret;
2702+
}
2703+
2704+
static int for_each_fullref_with_seek(struct ref_filter *filter, each_ref_fn cb,
2705+
void *cb_data, unsigned int flags)
2706+
{
2707+
struct ref_iterator *iter;
2708+
int ret = 0;
2709+
2710+
iter = refs_ref_iterator_begin(get_main_ref_store(the_repository), "",
2711+
NULL, 0, flags);
2712+
if (filter->start_after)
2713+
ret = start_ref_iterator_after(iter, filter->start_after);
2714+
2715+
if (ret)
2716+
return ret;
2717+
2718+
return do_for_each_ref_iterator(iter, cb, cb_data);
2719+
}
2720+
26862721
/*
26872722
* This is the same as for_each_fullref_in(), but it tries to iterate
26882723
* only over the patterns we'll care about. Note that it _doesn't_ do a full
@@ -2694,8 +2729,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
26942729
{
26952730
if (filter->kind & FILTER_REFS_ROOT_REFS) {
26962731
/* In this case, we want to print all refs including root refs. */
2697-
return refs_for_each_include_root_refs(get_main_ref_store(the_repository),
2698-
cb, cb_data);
2732+
return for_each_fullref_with_seek(filter, cb, cb_data,
2733+
DO_FOR_EACH_INCLUDE_ROOT_REFS);
26992734
}
27002735

27012736
if (!filter->match_as_path) {
@@ -2704,8 +2739,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
27042739
* prefixes like "refs/heads/" etc. are stripped off,
27052740
* so we have to look at everything:
27062741
*/
2707-
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
2708-
"", NULL, cb, cb_data);
2742+
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
27092743
}
27102744

27112745
if (filter->ignore_case) {
@@ -2714,14 +2748,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
27142748
* so just return everything and let the caller
27152749
* sort it out.
27162750
*/
2717-
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
2718-
"", NULL, cb, cb_data);
2751+
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
27192752
}
27202753

27212754
if (!filter->name_patterns[0]) {
27222755
/* no patterns; we have to look at everything */
2723-
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
2724-
"", filter->exclude.v, cb, cb_data);
2756+
return for_each_fullref_with_seek(filter, cb, cb_data, 0);
27252757
}
27262758

27272759
return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
@@ -3189,6 +3221,7 @@ void filter_is_base(struct repository *r,
31893221

31903222
static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
31913223
{
3224+
const char *prefix = NULL;
31923225
int ret = 0;
31933226

31943227
filter->kind = type & FILTER_REFS_KIND_MASK;
@@ -3199,38 +3232,47 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
31993232
/* Simple per-ref filtering */
32003233
if (!filter->kind)
32013234
die("filter_refs: invalid type");
3202-
else {
3203-
/*
3204-
* For common cases where we need only branches or remotes or tags,
3205-
* we only iterate through those refs. If a mix of refs is needed,
3206-
* we iterate over all refs and filter out required refs with the help
3207-
* of filter_ref_kind().
3208-
*/
3209-
if (filter->kind == FILTER_REFS_BRANCHES)
3210-
ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
3211-
"refs/heads/", NULL,
3212-
fn, cb_data);
3213-
else if (filter->kind == FILTER_REFS_REMOTES)
3214-
ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
3215-
"refs/remotes/", NULL,
3216-
fn, cb_data);
3217-
else if (filter->kind == FILTER_REFS_TAGS)
3218-
ret = refs_for_each_fullref_in(get_main_ref_store(the_repository),
3219-
"refs/tags/", NULL, fn,
3220-
cb_data);
3221-
else if (filter->kind & FILTER_REFS_REGULAR)
3222-
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
32233235

3224-
/*
3225-
* When printing all ref types, HEAD is already included,
3226-
* so we don't want to print HEAD again.
3227-
*/
3228-
if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) &&
3229-
(filter->kind & FILTER_REFS_DETACHED_HEAD))
3230-
refs_head_ref(get_main_ref_store(the_repository), fn,
3231-
cb_data);
3236+
/*
3237+
* For common cases where we need only branches or remotes or tags,
3238+
* we only iterate through those refs. If a mix of refs is needed,
3239+
* we iterate over all refs and filter out required refs with the help
3240+
* of filter_ref_kind().
3241+
*/
3242+
if (filter->kind == FILTER_REFS_BRANCHES)
3243+
prefix = "refs/heads/";
3244+
else if (filter->kind == FILTER_REFS_REMOTES)
3245+
prefix = "refs/remotes/";
3246+
else if (filter->kind == FILTER_REFS_TAGS)
3247+
prefix = "refs/tags/";
3248+
3249+
if (prefix) {
3250+
struct ref_iterator *iter;
3251+
3252+
iter = refs_ref_iterator_begin(get_main_ref_store(the_repository),
3253+
"", NULL, 0, 0);
3254+
3255+
if (filter->start_after)
3256+
ret = start_ref_iterator_after(iter, filter->start_after);
3257+
else if (prefix)
3258+
ret = ref_iterator_seek(iter, prefix, 1);
3259+
3260+
if (!ret)
3261+
ret = do_for_each_ref_iterator(iter, fn, cb_data);
3262+
} else if (filter->kind & FILTER_REFS_REGULAR) {
3263+
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
32323264
}
32333265

3266+
/*
3267+
* When printing all ref types, HEAD is already included,
3268+
* so we don't want to print HEAD again.
3269+
*/
3270+
if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) &&
3271+
(filter->kind & FILTER_REFS_DETACHED_HEAD))
3272+
refs_head_ref(get_main_ref_store(the_repository), fn,
3273+
cb_data);
3274+
3275+
32343276
clear_contains_cache(&filter->internal.contains_cache);
32353277
clear_contains_cache(&filter->internal.no_contains_cache);
32363278

ref-filter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct ref_array {
6464

6565
struct ref_filter {
6666
const char **name_patterns;
67+
const char *start_after;
6768
struct strvec exclude;
6869
struct oid_array points_at;
6970
struct commit_list *with_commit;

refs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,12 +2657,12 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
26572657
if (!initial_transaction) {
26582658
int ok;
26592659

2660-
if (!iter) {
2660+
if (!iter)
26612661
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
26622662
DO_FOR_EACH_INCLUDE_BROKEN);
2663-
} else if (ref_iterator_seek(iter, dirname.buf) < 0) {
2663+
else if (ref_iterator_seek(iter, dirname.buf,
2664+
REF_ITERATOR_SEEK_SET_PREFIX) < 0)
26642665
goto cleanup;
2665-
}
26662666

26672667
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
26682668
if (skip &&

0 commit comments

Comments
 (0)