The `git-for-each-ref(1)` command is used to iterate over references
present in a repository. In large repositories with millions of
references, it would be optimal to paginate this output such that we
can start iteration from a given reference. This would avoid having to
iterate over all references from the beginning each time when paginating
through results.
The previous commit added 'seek' functionality to the reference
backends. Utilize this and expose a '--start-after' option in
'git-for-each-ref(1)'. When used, the reference iteration seeks to the
lexicographically next reference and iterates from there onward.
This enables efficient pagination workflows, where the calling script
can remember the last provided reference and use that as the starting
point for the next set of references:
git for-each-ref --count=100
git for-each-ref --count=100 --start-after=refs/heads/branch-100
git for-each-ref --count=100 --start-after=refs/heads/branch-200
Since the reference iterators only allow seeking to a specified marker
via the `ref_iterator_seek()`, we introduce a helper function
`start_ref_iterator_after()`, which seeks to next reference by simply
adding (char) 1 to the marker.
We must note that pagination always continues from the provided marker,
as such any concurrent reference updates lexicographically behind the
marker will not be output. Document the same.
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
122 lines
4.3 KiB
C
122 lines
4.3 KiB
C
#include "builtin.h"
|
|
#include "commit.h"
|
|
#include "config.h"
|
|
#include "gettext.h"
|
|
#include "object.h"
|
|
#include "parse-options.h"
|
|
#include "ref-filter.h"
|
|
#include "strbuf.h"
|
|
#include "strvec.h"
|
|
|
|
static char const * const for_each_ref_usage[] = {
|
|
N_("git for-each-ref [<options>] [<pattern>]"),
|
|
N_("git for-each-ref [--points-at <object>]"),
|
|
N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"),
|
|
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
|
|
N_("git for-each-ref [--start-after <marker>]"),
|
|
NULL
|
|
};
|
|
|
|
int cmd_for_each_ref(int argc,
|
|
const char **argv,
|
|
const char *prefix,
|
|
struct repository *repo)
|
|
{
|
|
struct ref_sorting *sorting;
|
|
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
|
int icase = 0, include_root_refs = 0, from_stdin = 0;
|
|
struct ref_filter filter = REF_FILTER_INIT;
|
|
struct ref_format format = REF_FORMAT_INIT;
|
|
unsigned int flags = FILTER_REFS_REGULAR;
|
|
struct strvec vec = STRVEC_INIT;
|
|
|
|
struct option opts[] = {
|
|
OPT_BIT('s', "shell", &format.quote_style,
|
|
N_("quote placeholders suitably for shells"), QUOTE_SHELL),
|
|
OPT_BIT('p', "perl", &format.quote_style,
|
|
N_("quote placeholders suitably for perl"), QUOTE_PERL),
|
|
OPT_BIT(0 , "python", &format.quote_style,
|
|
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
|
|
OPT_BIT(0 , "tcl", &format.quote_style,
|
|
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
|
|
OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty,
|
|
N_("do not output a newline after empty formatted refs")),
|
|
|
|
OPT_GROUP(""),
|
|
OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
|
|
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
|
|
OPT_STRING( 0 , "start-after", &filter.start_after, N_("start-start"), N_("start iteration after the provided marker")),
|
|
OPT__COLOR(&format.use_color, N_("respect format colors")),
|
|
OPT_REF_FILTER_EXCLUDE(&filter),
|
|
OPT_REF_SORT(&sorting_options),
|
|
OPT_CALLBACK(0, "points-at", &filter.points_at,
|
|
N_("object"), N_("print only refs which points at the given object"),
|
|
parse_opt_object_name),
|
|
OPT_MERGED(&filter, N_("print only refs that are merged")),
|
|
OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
|
|
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
|
|
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
|
|
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
|
OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
|
|
OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
|
|
OPT_END(),
|
|
};
|
|
|
|
format.format = "%(objectname) %(objecttype)\t%(refname)";
|
|
|
|
repo_config(repo, git_default_config, NULL);
|
|
|
|
/* Set default (refname) sorting */
|
|
string_list_append(&sorting_options, "refname");
|
|
|
|
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
|
|
if (format.array_opts.max_count < 0) {
|
|
error("invalid --count argument: `%d'", format.array_opts.max_count);
|
|
usage_with_options(for_each_ref_usage, opts);
|
|
}
|
|
if (HAS_MULTI_BITS(format.quote_style)) {
|
|
error("more than one quoting style?");
|
|
usage_with_options(for_each_ref_usage, opts);
|
|
}
|
|
if (verify_ref_format(&format))
|
|
usage_with_options(for_each_ref_usage, opts);
|
|
|
|
if (filter.start_after && sorting_options.nr > 1)
|
|
die(_("cannot use --start-after with custom sort options"));
|
|
|
|
sorting = ref_sorting_options(&sorting_options);
|
|
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
|
|
filter.ignore_case = icase;
|
|
|
|
if (from_stdin) {
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
if (argv[0])
|
|
die(_("unknown arguments supplied with --stdin"));
|
|
|
|
while (strbuf_getline(&line, stdin) != EOF)
|
|
strvec_push(&vec, line.buf);
|
|
|
|
strbuf_release(&line);
|
|
|
|
/* vec.v is NULL-terminated, just like 'argv'. */
|
|
filter.name_patterns = vec.v;
|
|
} else {
|
|
filter.name_patterns = argv;
|
|
}
|
|
|
|
if (filter.start_after && filter.name_patterns && filter.name_patterns[0])
|
|
die(_("cannot use --start-after with patterns"));
|
|
|
|
if (include_root_refs)
|
|
flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;
|
|
|
|
filter.match_as_path = 1;
|
|
filter_and_format_refs(&filter, flags, sorting, &format);
|
|
|
|
ref_filter_clear(&filter);
|
|
ref_sorting_release(sorting);
|
|
strvec_clear(&vec);
|
|
return 0;
|
|
}
|