From 56d26ade97135614ccc60cb215d6c9cb22babfb1 Mon Sep 17 00:00:00 2001 From: Victoria Dye Date: Tue, 14 Nov 2023 19:53:49 +0000 Subject: ref-filter.c: really don't sort when using --no-sort When '--no-sort' is passed to 'for-each-ref', 'tag', and 'branch', the printed refs are still sorted by ascending refname. Change the handling of sort options in these commands so that '--no-sort' to truly disables sorting. '--no-sort' does not disable sorting in these commands is because their option parsing does not distinguish between "the absence of '--sort'" (and/or values for tag.sort & branch.sort) and '--no-sort'. Both result in an empty 'sorting_options' string list, which is parsed by 'ref_sorting_options()' to create the 'struct ref_sorting *' for the command. If the string list is empty, 'ref_sorting_options()' interprets that as "the absence of '--sort'" and returns the default ref sorting structure (equivalent to "refname" sort). To handle '--no-sort' properly while preserving the "refname" sort in the "absence of --sort'" case, first explicitly add "refname" to the string list *before* parsing options. This alone doesn't actually change any behavior, since 'compare_refs()' already falls back on comparing refnames if two refs are equal w.r.t all other sort keys. Now that the string list is populated by default, '--no-sort' is the only way to empty the 'sorting_options' string list. Update 'ref_sorting_options()' to return a NULL 'struct ref_sorting *' if the string list is empty, and add a condition to 'ref_array_sort()' to skip the sort altogether if the sort structure is NULL. Note that other functions using 'struct ref_sorting *' do not need any changes because they already ignore NULL values. Finally, remove the condition around sorting in 'ls-remote', since it's no longer necessary. Unlike 'for-each-ref' et. al., it does *not* do any sorting by default. This default is preserved by simply leaving its sort key string list empty before parsing options; if no additional sort keys are set, 'struct ref_sorting *' is NULL and sorting is skipped. Signed-off-by: Victoria Dye Signed-off-by: Junio C Hamano --- builtin/tag.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'builtin/tag.c') diff --git a/builtin/tag.c b/builtin/tag.c index 3918eacbb5..64f3196cd4 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -501,7 +501,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) setup_ref_filter_porcelain_msg(); + /* + * Try to set sort keys from config. If config does not set any, + * fall back on default (refname) sorting. + */ git_config(git_tag_config, &sorting_options); + if (!sorting_options.nr) + string_list_append(&sorting_options, "refname"); memset(&opt, 0, sizeof(opt)); filter.lines = -1; -- cgit v1.2.3 From 9d4fcfe1ff5b901f47f8226d078d22370bb955be Mon Sep 17 00:00:00 2001 From: Victoria Dye Date: Tue, 14 Nov 2023 19:53:50 +0000 Subject: ref-filter.h: add max_count and omit_empty to ref_format Add an internal 'array_opts' struct to 'struct ref_format' containing formatting options that pertain to the formatting of an entire ref array: 'max_count' and 'omit_empty'. These values are specified by the '--count' and '--omit-empty' options, respectively, to 'for-each-ref'/'tag'/'branch'. Storing these values in the 'ref_format' will simplify the consolidation of ref array formatting logic across builtins in later patches. Signed-off-by: Victoria Dye Signed-off-by: Junio C Hamano --- builtin/branch.c | 5 ++--- builtin/for-each-ref.c | 21 +++++++++++---------- builtin/tag.c | 5 ++--- ref-filter.h | 5 +++++ 4 files changed, 20 insertions(+), 16 deletions(-) (limited to 'builtin/tag.c') diff --git a/builtin/branch.c b/builtin/branch.c index 2da43f17e4..49198b548e 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -45,7 +45,6 @@ static const char *head; static struct object_id head_oid; static int recurse_submodules = 0; static int submodule_propagate_branches = 0; -static int omit_empty = 0; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { @@ -480,7 +479,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin string_list_append(output, out.buf); } else { fwrite(out.buf, 1, out.len, stdout); - if (out.len || !omit_empty) + if (out.len || !format->array_opts.omit_empty) putchar('\n'); } } @@ -737,7 +736,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2), - OPT_BOOL(0, "omit-empty", &omit_empty, + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, N_("do not output a newline after empty formatted refs")), OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1), OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2), diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 93b370f550..881c3ee055 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -19,10 +19,10 @@ static char const * const for_each_ref_usage[] = { int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { - int i; + int i, total; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; - int maxcount = 0, icase = 0, omit_empty = 0; + int icase = 0; struct ref_array array; struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; @@ -40,11 +40,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) 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", &omit_empty, + 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", &maxcount, N_("show only matched refs")), + OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only matched refs")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), OPT__COLOR(&format.use_color, N_("respect format colors")), OPT_REF_FILTER_EXCLUDE(&filter), @@ -71,8 +71,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) string_list_append(&sorting_options, "refname"); parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); - if (maxcount < 0) { - error("invalid --count argument: `%d'", maxcount); + 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)) { @@ -109,15 +109,16 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) ref_array_sort(sorting, &array); - if (!maxcount || array.nr < maxcount) - maxcount = array.nr; - for (i = 0; i < maxcount; i++) { + total = format.array_opts.max_count; + if (!total || array.nr < total) + total = array.nr; + for (i = 0; i < total; i++) { strbuf_reset(&err); strbuf_reset(&output); if (format_ref_array_item(array.items[i], &format, &output, &err)) die("%s", err.buf); fwrite(output.buf, 1, output.len, stdout); - if (output.len || !omit_empty) + if (output.len || !format.array_opts.omit_empty) putchar('\n'); } diff --git a/builtin/tag.c b/builtin/tag.c index 64f3196cd4..2d599245d4 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -44,7 +44,6 @@ static const char * const git_tag_usage[] = { static unsigned int colopts; static int force_sign_annotate; static int config_sign_tag = -1; /* unspecified */ -static int omit_empty = 0; static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format) @@ -83,7 +82,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, if (format_ref_array_item(array.items[i], format, &output, &err)) die("%s", err.buf); fwrite(output.buf, 1, output.len, stdout); - if (output.len || !omit_empty) + if (output.len || !format->array_opts.omit_empty) putchar('\n'); } @@ -481,7 +480,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")), OPT_MERGED(&filter, N_("print only tags that are merged")), OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), - OPT_BOOL(0, "omit-empty", &omit_empty, + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, N_("do not output a newline after empty formatted refs")), OPT_REF_SORT(&sorting_options), { diff --git a/ref-filter.h b/ref-filter.h index 1524bc463a..d87d61238b 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -92,6 +92,11 @@ struct ref_format { /* List of bases for ahead-behind counts. */ struct string_list bases; + + struct { + int max_count; + int omit_empty; + } array_opts; }; #define REF_FILTER_INIT { \ -- cgit v1.2.3 From e7574b0c6b1e0cf2de9e000766a13d23fd9516e6 Mon Sep 17 00:00:00 2001 From: Victoria Dye Date: Tue, 14 Nov 2023 19:53:52 +0000 Subject: ref-filter.h: add functions for filter/format & format-only Add two new public methods to 'ref-filter.h': * 'print_formatted_ref_array()' which, given a format specification & array of ref items, formats and prints the items to stdout. * 'filter_and_format_refs()' which combines 'filter_refs()', 'ref_array_sort()', and 'print_formatted_ref_array()' into a single function. This consolidates much of the code used to filter and format refs in 'builtin/for-each-ref.c', 'builtin/tag.c', and 'builtin/branch.c', reducing duplication and simplifying the future changes needed to optimize the filter & format process. Signed-off-by: Victoria Dye Signed-off-by: Junio C Hamano --- builtin/branch.c | 33 +++++++++++++++++---------------- builtin/for-each-ref.c | 27 +-------------------------- builtin/tag.c | 23 +---------------------- ref-filter.c | 35 +++++++++++++++++++++++++++++++++++ ref-filter.h | 14 ++++++++++++++ 5 files changed, 68 insertions(+), 64 deletions(-) (limited to 'builtin/tag.c') diff --git a/builtin/branch.c b/builtin/branch.c index 49198b548e..40da31e640 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -437,8 +437,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin { int i; struct ref_array array; - struct strbuf out = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; int maxwidth = 0; const char *remote_prefix = ""; char *to_free = NULL; @@ -468,24 +466,27 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin filter_ahead_behind(the_repository, format, &array); ref_array_sort(sorting, &array); - for (i = 0; i < array.nr; i++) { - strbuf_reset(&err); - strbuf_reset(&out); - if (format_ref_array_item(array.items[i], format, &out, &err)) - die("%s", err.buf); - if (column_active(colopts)) { - assert(!filter->verbose && "--column and --verbose are incompatible"); - /* format to a string_list to let print_columns() do its job */ + if (column_active(colopts)) { + struct strbuf out = STRBUF_INIT, err = STRBUF_INIT; + + assert(!filter->verbose && "--column and --verbose are incompatible"); + + for (i = 0; i < array.nr; i++) { + strbuf_reset(&err); + strbuf_reset(&out); + if (format_ref_array_item(array.items[i], format, &out, &err)) + die("%s", err.buf); + + /* format to a string_list to let print_columns() do its job */ string_list_append(output, out.buf); - } else { - fwrite(out.buf, 1, out.len, stdout); - if (out.len || !format->array_opts.omit_empty) - putchar('\n'); } + + strbuf_release(&err); + strbuf_release(&out); + } else { + print_formatted_ref_array(&array, format); } - strbuf_release(&err); - strbuf_release(&out); ref_array_clear(&array); free(to_free); } diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 881c3ee055..1c19cd5bd3 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -19,15 +19,11 @@ static char const * const for_each_ref_usage[] = { int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { - int i, total; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; int icase = 0; - struct ref_array array; struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; int from_stdin = 0; struct strvec vec = STRVEC_INIT; @@ -61,8 +57,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_END(), }; - memset(&array, 0, sizeof(array)); - format.format = "%(objectname) %(objecttype)\t%(refname)"; git_config(git_default_config, NULL); @@ -104,27 +98,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) } filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL); - filter_ahead_behind(the_repository, &format, &array); - - ref_array_sort(sorting, &array); - - total = format.array_opts.max_count; - if (!total || array.nr < total) - total = array.nr; - for (i = 0; i < total; i++) { - strbuf_reset(&err); - strbuf_reset(&output); - if (format_ref_array_item(array.items[i], &format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - if (output.len || !format.array_opts.omit_empty) - putchar('\n'); - } + filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format); - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); ref_filter_clear(&filter); ref_sorting_release(sorting); strvec_clear(&vec); diff --git a/builtin/tag.c b/builtin/tag.c index 2d599245d4..2528d499dd 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -48,13 +48,7 @@ static int config_sign_tag = -1; /* unspecified */ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format) { - struct ref_array array; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; char *to_free = NULL; - int i; - - memset(&array, 0, sizeof(array)); if (filter->lines == -1) filter->lines = 0; @@ -72,23 +66,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, if (verify_ref_format(format)) die(_("unable to parse format string")); filter->with_commit_tag_algo = 1; - filter_refs(&array, filter, FILTER_REFS_TAGS); - filter_ahead_behind(the_repository, format, &array); - ref_array_sort(sorting, &array); - - for (i = 0; i < array.nr; i++) { - strbuf_reset(&output); - strbuf_reset(&err); - if (format_ref_array_item(array.items[i], format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - if (output.len || !format->array_opts.omit_empty) - putchar('\n'); - } + filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format); - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); free(to_free); return 0; diff --git a/ref-filter.c b/ref-filter.c index 3de4cefcf5..f062bb4938 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2939,6 +2939,18 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int return ret; } +void filter_and_format_refs(struct ref_filter *filter, unsigned int type, + struct ref_sorting *sorting, + struct ref_format *format) +{ + struct ref_array array = { 0 }; + filter_refs(&array, filter, type); + filter_ahead_behind(the_repository, format, &array); + ref_array_sort(sorting, &array); + print_formatted_ref_array(&array, format); + ref_array_clear(&array); +} + static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b) { if (!(a->kind ^ b->kind)) @@ -3128,6 +3140,29 @@ int format_ref_array_item(struct ref_array_item *info, return 0; } +void print_formatted_ref_array(struct ref_array *array, struct ref_format *format) +{ + int total; + struct strbuf output = STRBUF_INIT, err = STRBUF_INIT; + + total = format->array_opts.max_count; + if (!total || array->nr < total) + total = array->nr; + for (int i = 0; i < total; i++) { + strbuf_reset(&err); + strbuf_reset(&output); + if (format_ref_array_item(array->items[i], format, &output, &err)) + die("%s", err.buf); + if (output.len || !format->array_opts.omit_empty) { + fwrite(output.buf, 1, output.len, stdout); + putchar('\n'); + } + } + + strbuf_release(&err); + strbuf_release(&output); +} + void pretty_print_ref(const char *name, const struct object_id *oid, struct ref_format *format) { diff --git a/ref-filter.h b/ref-filter.h index 0db3ff5288..0ce5af58ab 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -137,6 +137,14 @@ struct ref_format { * filtered refs in the ref_array structure. */ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type); +/* + * Filter refs using the given ref_filter and type, sort the contents + * according to the given ref_sorting, format the filtered refs with the + * given ref_format, and print them to stdout. + */ +void filter_and_format_refs(struct ref_filter *filter, unsigned int type, + struct ref_sorting *sorting, + struct ref_format *format); /* Clear all memory allocated to ref_array */ void ref_array_clear(struct ref_array *array); /* Used to verify if the given format is correct and to parse out the used atoms */ @@ -161,6 +169,12 @@ char *get_head_description(void); /* Set up translated strings in the output. */ void setup_ref_filter_porcelain_msg(void); +/* + * Print up to maxcount ref_array elements to stdout using the given + * ref_format. + */ +void print_formatted_ref_array(struct ref_array *array, struct ref_format *format); + /* * Print a single ref, outside of any ref-filter. Note that the * name must be a fully qualified refname. -- cgit v1.2.3