Skip to content

Commit 59a5de8

Browse files
committed
Merge branch 'kn/for-each-ref-skip' into seen
* kn/for-each-ref-skip: for-each-ref: introduce a '--skip-until' option 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 9d082cd + 83757c7 commit 59a5de8

15 files changed

+507
-221
lines changed

Documentation/git-for-each-ref.adoc

Lines changed: 5 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> ...] [--skip-until=<pattern>]
1818

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

111+
--skip-until::
112+
Skip references up to the specified pattern. Cannot be used with
113+
general pattern matching.
114+
111115
FIELD NAMES
112116
-----------
113117

builtin/for-each-ref.c

Lines changed: 5 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 [--skip-until <pattern>]"),
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 , "skip-until", &filter.seek, N_("skip-until"), N_("skip references until")),
4749
OPT__COLOR(&format.use_color, N_("respect format colors")),
4850
OPT_REF_FILTER_EXCLUDE(&filter),
4951
OPT_REF_SORT(&sorting_options),
@@ -100,6 +102,9 @@ int cmd_for_each_ref(int argc,
100102
filter.name_patterns = argv;
101103
}
102104

105+
if (filter.seek && filter.name_patterns && filter.name_patterns[0])
106+
die(_("cannot use --skip-until with patterns"));
107+
103108
if (include_root_refs)
104109
flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;
105110

ref-filter.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2692,10 +2692,13 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
26922692
each_ref_fn cb,
26932693
void *cb_data)
26942694
{
2695+
struct ref_iterator *iter;
2696+
int flags = 0, ret = 0;
2697+
26952698
if (filter->kind & FILTER_REFS_ROOT_REFS) {
26962699
/* 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);
2700+
flags |= DO_FOR_EACH_INCLUDE_ROOT_REFS;
2701+
goto non_prefix_iter;
26992702
}
27002703

27012704
if (!filter->match_as_path) {
@@ -2704,8 +2707,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
27042707
* prefixes like "refs/heads/" etc. are stripped off,
27052708
* so we have to look at everything:
27062709
*/
2707-
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
2708-
"", NULL, cb, cb_data);
2710+
goto non_prefix_iter;
27092711
}
27102712

27112713
if (filter->ignore_case) {
@@ -2714,20 +2716,28 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
27142716
* so just return everything and let the caller
27152717
* sort it out.
27162718
*/
2717-
return refs_for_each_fullref_in(get_main_ref_store(the_repository),
2718-
"", NULL, cb, cb_data);
2719+
goto non_prefix_iter;
27192720
}
27202721

27212722
if (!filter->name_patterns[0]) {
27222723
/* 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);
2724+
goto non_prefix_iter;
27252725
}
27262726

27272727
return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
27282728
NULL, filter->name_patterns,
27292729
filter->exclude.v,
27302730
cb, cb_data);
2731+
2732+
non_prefix_iter:
2733+
iter = refs_ref_iterator_begin(get_main_ref_store(the_repository), "",
2734+
NULL, 0, flags);
2735+
if (filter->seek)
2736+
ret = ref_iterator_seek(iter, filter->seek, 0);
2737+
if (ret)
2738+
return ret;
2739+
2740+
return do_for_each_ref_iterator(iter, cb, cb_data);
27312741
}
27322742

27332743
/*
@@ -3200,26 +3210,37 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
32003210
if (!filter->kind)
32013211
die("filter_refs: invalid type");
32023212
else {
3213+
const char *prefix = NULL;
3214+
32033215
/*
32043216
* For common cases where we need only branches or remotes or tags,
32053217
* we only iterate through those refs. If a mix of refs is needed,
32063218
* we iterate over all refs and filter out required refs with the help
32073219
* of filter_ref_kind().
32083220
*/
32093221
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);
3222+
prefix = "refs/heads/";
32133223
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);
3224+
prefix = "refs/remotes/";
32173225
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)
3226+
prefix = "refs/tags/";
3227+
3228+
if (prefix) {
3229+
struct ref_iterator *iter;
3230+
3231+
iter = refs_ref_iterator_begin(get_main_ref_store(the_repository),
3232+
"", NULL, 0, 0);
3233+
3234+
if (filter->seek)
3235+
ret = ref_iterator_seek(iter, filter->seek, 0);
3236+
else if (prefix)
3237+
ret = ref_iterator_seek(iter, prefix, 1);
3238+
3239+
if (!ret)
3240+
ret = do_for_each_ref_iterator(iter, fn, cb_data);
3241+
} else if (filter->kind & FILTER_REFS_REGULAR) {
32223242
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
3243+
}
32233244

32243245
/*
32253246
* When printing all ref types, HEAD is already included,

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 *seek;
6768
struct strvec exclude;
6869
struct oid_array points_at;
6970
struct commit_list *with_commit;

refs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2657,7 +2657,7 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs
26572657
if (!iter) {
26582658
iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
26592659
DO_FOR_EACH_INCLUDE_BROKEN);
2660-
} else if (ref_iterator_seek(iter, dirname.buf) < 0) {
2660+
} else if (ref_iterator_seek(iter, dirname.buf, 1) < 0) {
26612661
goto cleanup;
26622662
}
26632663

refs.h

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,4 +1195,157 @@ int repo_migrate_ref_storage_format(struct repository *repo,
11951195
unsigned int flags,
11961196
struct strbuf *err);
11971197

1198+
/*
1199+
* Reference iterators
1200+
*
1201+
* A reference iterator encapsulates the state of an in-progress
1202+
* iteration over references. Create an instance of `struct
1203+
* ref_iterator` via one of the functions in this module.
1204+
*
1205+
* A freshly-created ref_iterator doesn't yet point at a reference. To
1206+
* advance the iterator, call ref_iterator_advance(). If successful,
1207+
* this sets the iterator's refname, oid, and flags fields to describe
1208+
* the next reference and returns ITER_OK. The data pointed at by
1209+
* refname and oid belong to the iterator; if you want to retain them
1210+
* after calling ref_iterator_advance() again or calling
1211+
* ref_iterator_free(), you must make a copy. When the iteration has
1212+
* been exhausted, ref_iterator_advance() releases any resources
1213+
* associated with the iteration, frees the ref_iterator object, and
1214+
* returns ITER_DONE. If you want to abort the iteration early, call
1215+
* ref_iterator_free(), which also frees the ref_iterator object and
1216+
* any associated resources. If there was an internal error advancing
1217+
* to the next entry, ref_iterator_advance() aborts the iteration,
1218+
* frees the ref_iterator, and returns ITER_ERROR.
1219+
*
1220+
* The reference currently being looked at can be peeled by calling
1221+
* ref_iterator_peel(). This function is often faster than peel_ref(),
1222+
* so it should be preferred when iterating over references.
1223+
*
1224+
* Putting it all together, a typical iteration looks like this:
1225+
*
1226+
* int ok;
1227+
* struct ref_iterator *iter = ...;
1228+
*
1229+
* while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
1230+
* if (want_to_stop_iteration()) {
1231+
* ok = ITER_DONE;
1232+
* break;
1233+
* }
1234+
*
1235+
* // Access information about the current reference:
1236+
* if (!(iter->flags & REF_ISSYMREF))
1237+
* printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
1238+
*
1239+
* // If you need to peel the reference:
1240+
* ref_iterator_peel(iter, &oid);
1241+
* }
1242+
*
1243+
* if (ok != ITER_DONE)
1244+
* handle_error();
1245+
* ref_iterator_free(iter);
1246+
*/
1247+
struct ref_iterator;
1248+
1249+
/*
1250+
* These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
1251+
* which feeds it).
1252+
*/
1253+
enum do_for_each_ref_flags {
1254+
/*
1255+
* Include broken references in a do_for_each_ref*() iteration, which
1256+
* would normally be omitted. This includes both refs that point to
1257+
* missing objects (a true repository corruption), ones with illegal
1258+
* names (which we prefer not to expose to callers), as well as
1259+
* dangling symbolic refs (i.e., those that point to a non-existent
1260+
* ref; this is not a corruption, but as they have no valid oid, we
1261+
* omit them from normal iteration results).
1262+
*/
1263+
DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0),
1264+
1265+
/*
1266+
* Only include per-worktree refs in a do_for_each_ref*() iteration.
1267+
* Normally this will be used with a files ref_store, since that's
1268+
* where all reference backends will presumably store their
1269+
* per-worktree refs.
1270+
*/
1271+
DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1),
1272+
1273+
/*
1274+
* Omit dangling symrefs from output; this only has an effect with
1275+
* INCLUDE_BROKEN, since they are otherwise not included at all.
1276+
*/
1277+
DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
1278+
1279+
/*
1280+
* Include root refs i.e. HEAD and pseudorefs along with the regular
1281+
* refs.
1282+
*/
1283+
DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
1284+
};
1285+
1286+
/*
1287+
* Return an iterator that goes over each reference in `refs` for
1288+
* which the refname begins with prefix. If trim is non-zero, then
1289+
* trim that many characters off the beginning of each refname.
1290+
* The output is ordered by refname.
1291+
*/
1292+
struct ref_iterator *refs_ref_iterator_begin(
1293+
struct ref_store *refs,
1294+
const char *prefix, const char **exclude_patterns,
1295+
int trim, enum do_for_each_ref_flags flags);
1296+
1297+
/*
1298+
* Advance the iterator to the first or next item and return ITER_OK.
1299+
* If the iteration is exhausted, free the resources associated with
1300+
* the ref_iterator and return ITER_DONE. On errors, free the iterator
1301+
* resources and return ITER_ERROR. It is a bug to use ref_iterator or
1302+
* call this function again after it has returned ITER_DONE or
1303+
* ITER_ERROR.
1304+
*/
1305+
int ref_iterator_advance(struct ref_iterator *ref_iterator);
1306+
1307+
/*
1308+
* Seek the iterator to the first reference matching the given seek string.
1309+
* The seek string is matched as a literal string, without regard for path
1310+
* separators. If prefix is NULL or the empty string, seek the iterator to the
1311+
* first reference again.
1312+
*
1313+
* When set_prefix is true, this function behaves as if a new ref iterator
1314+
* with the same prefix had been created, setting the prefix for subsequent
1315+
* iteration. When set_prefix is false, the iterator simply seeks to the
1316+
* specified reference without changing the existing prefix, allowing
1317+
* iteration to start from that specific reference.
1318+
*
1319+
* This function allows reuse of iterators and thus may allow the backend
1320+
* to optimize. Parameters other than the prefix that have been passed when
1321+
* creating the iterator will remain unchanged.
1322+
*
1323+
* Returns 0 on success, a negative error code otherwise.
1324+
*/
1325+
int ref_iterator_seek(struct ref_iterator *ref_iterator,
1326+
const char *seek, int set_prefix);
1327+
1328+
/*
1329+
* If possible, peel the reference currently being viewed by the
1330+
* iterator. Return 0 on success.
1331+
*/
1332+
int ref_iterator_peel(struct ref_iterator *ref_iterator,
1333+
struct object_id *peeled);
1334+
1335+
/* Free the reference iterator and any associated resources. */
1336+
void ref_iterator_free(struct ref_iterator *ref_iterator);
1337+
1338+
/*
1339+
* The common backend for the for_each_*ref* functions. Call fn for
1340+
* each reference in iter. If the iterator itself ever returns
1341+
* ITER_ERROR, return -1. If fn ever returns a non-zero value, stop
1342+
* the iteration and return that value. Otherwise, return 0. In any
1343+
* case, free the iterator when done. This function is basically an
1344+
* adapter between the callback style of reference iteration and the
1345+
* iterator style.
1346+
*/
1347+
int do_for_each_ref_iterator(struct ref_iterator *iter,
1348+
each_ref_fn fn, void *cb_data);
1349+
1350+
11981351
#endif /* REFS_H */

refs/debug.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,13 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
170170
}
171171

172172
static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
173-
const char *prefix)
173+
const char *seek, int set_prefix)
174174
{
175175
struct debug_ref_iterator *diter =
176176
(struct debug_ref_iterator *)ref_iterator;
177-
int res = diter->iter->vtable->seek(diter->iter, prefix);
178-
trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res);
177+
int res = diter->iter->vtable->seek(diter->iter, seek, set_prefix);
178+
trace_printf_key(&trace_refs, "iterator_seek: %s set_prefix: %d: %d\n",
179+
seek ? seek : "", set_prefix, res);
179180
return res;
180181
}
181182

refs/files-backend.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -929,11 +929,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
929929
}
930930

931931
static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
932-
const char *prefix)
932+
const char *seek, int set_prefix)
933933
{
934934
struct files_ref_iterator *iter =
935935
(struct files_ref_iterator *)ref_iterator;
936-
return ref_iterator_seek(iter->iter0, prefix);
936+
return ref_iterator_seek(iter->iter0, seek, set_prefix);
937937
}
938938

939939
static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
@@ -2316,7 +2316,8 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
23162316
}
23172317

23182318
static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
2319-
const char *prefix UNUSED)
2319+
const char *seek UNUSED,
2320+
int set_prefix UNUSED)
23202321
{
23212322
BUG("ref_iterator_seek() called for reflog_iterator");
23222323
}

0 commit comments

Comments
 (0)