Skip to content

Commit 98d0a1f

Browse files
committed
Merge branch 'vd/for-each-ref-unsorted-optimization'
"git for-each-ref --no-sort" still sorted the refs alphabetically which paid non-trivial cost. It has been redefined to show output in an unspecified order, to allow certain optimizations to take advantage of. * vd/for-each-ref-unsorted-optimization: t/perf: add perf tests for for-each-ref ref-filter.c: use peeled tag for '*' format fields for-each-ref: clean up documentation of --format ref-filter.c: filter & format refs in the same callback ref-filter.c: refactor to create common helper functions ref-filter.c: rename 'ref_filter_handler()' to 'filter_one()' ref-filter.h: add functions for filter/format & format-only ref-filter.h: move contains caches into filter ref-filter.h: add max_count and omit_empty to ref_format ref-filter.c: really don't sort when using --no-sort
2 parents e020e55 + 294bfc2 commit 98d0a1f

12 files changed

+517
-174
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,14 @@ OPTIONS
5151
key.
5252

5353
--format=<format>::
54-
A string that interpolates `%(fieldname)` from a ref being shown
55-
and the object it points at. If `fieldname`
56-
is prefixed with an asterisk (`*`) and the ref points
57-
at a tag object, use the value for the field in the object
58-
which the tag object refers to (instead of the field in the tag object).
59-
When unspecified, `<format>` defaults to
60-
`%(objectname) SPC %(objecttype) TAB %(refname)`.
61-
It also interpolates `%%` to `%`, and `%xx` where `xx`
62-
are hex digits interpolates to character with hex code
63-
`xx`; for example `%00` interpolates to `\0` (NUL),
64-
`%09` to `\t` (TAB) and `%0a` to `\n` (LF).
54+
A string that interpolates `%(fieldname)` from a ref being shown and
55+
the object it points at. In addition, the string literal `%%`
56+
renders as `%` and `%xx` - where `xx` are hex digits - renders as
57+
the character with hex code `xx`. For example, `%00` interpolates to
58+
`\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
59+
+
60+
When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
61+
TAB %(refname)`.
6562

6663
--color[=<when>]::
6764
Respect any colors specified in the `--format` option. The
@@ -298,6 +295,10 @@ fields will correspond to the appropriate date or name-email-date tuple
298295
from the `committer` or `tagger` fields depending on the object type.
299296
These are intended for working on a mix of annotated and lightweight tags.
300297

298+
For tag objects, a `fieldname` prefixed with an asterisk (`*`) expands to
299+
the `fieldname` value of the peeled object, rather than that of the tag
300+
object itself.
301+
301302
Fields that have name-email-date tuple as its value (`author`,
302303
`committer`, and `tagger`) can be suffixed with `name`, `email`,
303304
and `date` to extract the named component. For email fields (`authoremail`,

builtin/branch.c

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ static const char *head;
4545
static struct object_id head_oid;
4646
static int recurse_submodules = 0;
4747
static int submodule_propagate_branches = 0;
48-
static int omit_empty = 0;
4948

5049
static int branch_use_color = -1;
5150
static char branch_colors[][COLOR_MAXLEN] = {
@@ -438,8 +437,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
438437
{
439438
int i;
440439
struct ref_array array;
441-
struct strbuf out = STRBUF_INIT;
442-
struct strbuf err = STRBUF_INIT;
443440
int maxwidth = 0;
444441
const char *remote_prefix = "";
445442
char *to_free = NULL;
@@ -469,24 +466,27 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
469466
filter_ahead_behind(the_repository, format, &array);
470467
ref_array_sort(sorting, &array);
471468

472-
for (i = 0; i < array.nr; i++) {
473-
strbuf_reset(&err);
474-
strbuf_reset(&out);
475-
if (format_ref_array_item(array.items[i], format, &out, &err))
476-
die("%s", err.buf);
477-
if (column_active(colopts)) {
478-
assert(!filter->verbose && "--column and --verbose are incompatible");
479-
/* format to a string_list to let print_columns() do its job */
469+
if (column_active(colopts)) {
470+
struct strbuf out = STRBUF_INIT, err = STRBUF_INIT;
471+
472+
assert(!filter->verbose && "--column and --verbose are incompatible");
473+
474+
for (i = 0; i < array.nr; i++) {
475+
strbuf_reset(&err);
476+
strbuf_reset(&out);
477+
if (format_ref_array_item(array.items[i], format, &out, &err))
478+
die("%s", err.buf);
479+
480+
/* format to a string_list to let print_columns() do its job */
480481
string_list_append(output, out.buf);
481-
} else {
482-
fwrite(out.buf, 1, out.len, stdout);
483-
if (out.len || !omit_empty)
484-
putchar('\n');
485482
}
483+
484+
strbuf_release(&err);
485+
strbuf_release(&out);
486+
} else {
487+
print_formatted_ref_array(&array, format);
486488
}
487489

488-
strbuf_release(&err);
489-
strbuf_release(&out);
490490
ref_array_clear(&array);
491491
free(to_free);
492492
}
@@ -737,7 +737,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
737737
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
738738
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
739739
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
740-
OPT_BOOL(0, "omit-empty", &omit_empty,
740+
OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty,
741741
N_("do not output a newline after empty formatted refs")),
742742
OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
743743
OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
@@ -767,7 +767,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
767767
if (argc == 2 && !strcmp(argv[1], "-h"))
768768
usage_with_options(builtin_branch_usage, options);
769769

770+
/*
771+
* Try to set sort keys from config. If config does not set any,
772+
* fall back on default (refname) sorting.
773+
*/
770774
git_config(git_branch_config, &sorting_options);
775+
if (!sorting_options.nr)
776+
string_list_append(&sorting_options, "refname");
771777

772778
track = git_branch_track;
773779

builtin/for-each-ref.c

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ static char const * const for_each_ref_usage[] = {
1919

2020
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
2121
{
22-
int i;
2322
struct ref_sorting *sorting;
2423
struct string_list sorting_options = STRING_LIST_INIT_DUP;
25-
int maxcount = 0, icase = 0, omit_empty = 0;
26-
struct ref_array array;
24+
int icase = 0;
2725
struct ref_filter filter = REF_FILTER_INIT;
2826
struct ref_format format = REF_FORMAT_INIT;
29-
struct strbuf output = STRBUF_INIT;
30-
struct strbuf err = STRBUF_INIT;
3127
int from_stdin = 0;
3228
struct strvec vec = STRVEC_INIT;
3329

@@ -40,11 +36,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
4036
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
4137
OPT_BIT(0 , "tcl", &format.quote_style,
4238
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
43-
OPT_BOOL(0, "omit-empty", &omit_empty,
39+
OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty,
4440
N_("do not output a newline after empty formatted refs")),
4541

4642
OPT_GROUP(""),
47-
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
43+
OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
4844
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
4945
OPT__COLOR(&format.use_color, N_("respect format colors")),
5046
OPT_REF_FILTER_EXCLUDE(&filter),
@@ -61,15 +57,16 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
6157
OPT_END(),
6258
};
6359

64-
memset(&array, 0, sizeof(array));
65-
6660
format.format = "%(objectname) %(objecttype)\t%(refname)";
6761

6862
git_config(git_default_config, NULL);
6963

64+
/* Set default (refname) sorting */
65+
string_list_append(&sorting_options, "refname");
66+
7067
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
71-
if (maxcount < 0) {
72-
error("invalid --count argument: `%d'", maxcount);
68+
if (format.array_opts.max_count < 0) {
69+
error("invalid --count argument: `%d'", format.array_opts.max_count);
7370
usage_with_options(for_each_ref_usage, opts);
7471
}
7572
if (HAS_MULTI_BITS(format.quote_style)) {
@@ -101,26 +98,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
10198
}
10299

103100
filter.match_as_path = 1;
104-
filter_refs(&array, &filter, FILTER_REFS_ALL);
105-
filter_ahead_behind(the_repository, &format, &array);
106-
107-
ref_array_sort(sorting, &array);
108-
109-
if (!maxcount || array.nr < maxcount)
110-
maxcount = array.nr;
111-
for (i = 0; i < maxcount; i++) {
112-
strbuf_reset(&err);
113-
strbuf_reset(&output);
114-
if (format_ref_array_item(array.items[i], &format, &output, &err))
115-
die("%s", err.buf);
116-
fwrite(output.buf, 1, output.len, stdout);
117-
if (output.len || !omit_empty)
118-
putchar('\n');
119-
}
101+
filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
120102

121-
strbuf_release(&err);
122-
strbuf_release(&output);
123-
ref_array_clear(&array);
124103
ref_filter_clear(&filter);
125104
ref_sorting_release(sorting);
126105
strvec_clear(&vec);

builtin/ls-remote.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
5858
struct transport *transport;
5959
const struct ref *ref;
6060
struct ref_array ref_array;
61+
struct ref_sorting *sorting;
6162
struct string_list sorting_options = STRING_LIST_INIT_DUP;
6263

6364
struct option options[] = {
@@ -141,13 +142,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
141142
item->symref = xstrdup_or_null(ref->symref);
142143
}
143144

144-
if (sorting_options.nr) {
145-
struct ref_sorting *sorting;
146-
147-
sorting = ref_sorting_options(&sorting_options);
148-
ref_array_sort(sorting, &ref_array);
149-
ref_sorting_release(sorting);
150-
}
145+
sorting = ref_sorting_options(&sorting_options);
146+
ref_array_sort(sorting, &ref_array);
151147

152148
for (i = 0; i < ref_array.nr; i++) {
153149
const struct ref_array_item *ref = ref_array.items[i];
@@ -157,6 +153,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
157153
status = 0; /* we found something */
158154
}
159155

156+
ref_sorting_release(sorting);
160157
ref_array_clear(&ref_array);
161158
if (transport_disconnect(transport))
162159
status = 1;

builtin/tag.c

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,11 @@ static const char * const git_tag_usage[] = {
4444
static unsigned int colopts;
4545
static int force_sign_annotate;
4646
static int config_sign_tag = -1; /* unspecified */
47-
static int omit_empty = 0;
4847

4948
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
5049
struct ref_format *format)
5150
{
52-
struct ref_array array;
53-
struct strbuf output = STRBUF_INIT;
54-
struct strbuf err = STRBUF_INIT;
5551
char *to_free = NULL;
56-
int i;
57-
58-
memset(&array, 0, sizeof(array));
5952

6053
if (filter->lines == -1)
6154
filter->lines = 0;
@@ -73,23 +66,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
7366
if (verify_ref_format(format))
7467
die(_("unable to parse format string"));
7568
filter->with_commit_tag_algo = 1;
76-
filter_refs(&array, filter, FILTER_REFS_TAGS);
77-
filter_ahead_behind(the_repository, format, &array);
78-
ref_array_sort(sorting, &array);
79-
80-
for (i = 0; i < array.nr; i++) {
81-
strbuf_reset(&output);
82-
strbuf_reset(&err);
83-
if (format_ref_array_item(array.items[i], format, &output, &err))
84-
die("%s", err.buf);
85-
fwrite(output.buf, 1, output.len, stdout);
86-
if (output.len || !omit_empty)
87-
putchar('\n');
88-
}
69+
filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format);
8970

90-
strbuf_release(&err);
91-
strbuf_release(&output);
92-
ref_array_clear(&array);
9371
free(to_free);
9472

9573
return 0;
@@ -481,7 +459,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
481459
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
482460
OPT_MERGED(&filter, N_("print only tags that are merged")),
483461
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
484-
OPT_BOOL(0, "omit-empty", &omit_empty,
462+
OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty,
485463
N_("do not output a newline after empty formatted refs")),
486464
OPT_REF_SORT(&sorting_options),
487465
{
@@ -501,7 +479,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
501479

502480
setup_ref_filter_porcelain_msg();
503481

482+
/*
483+
* Try to set sort keys from config. If config does not set any,
484+
* fall back on default (refname) sorting.
485+
*/
504486
git_config(git_tag_config, &sorting_options);
487+
if (!sorting_options.nr)
488+
string_list_append(&sorting_options, "refname");
505489

506490
memset(&opt, 0, sizeof(opt));
507491
filter.lines = -1;

0 commit comments

Comments
 (0)