summaryrefslogtreecommitdiff
path: root/ref-filter.c
diff options
context:
space:
mode:
authorVictoria Dye <vdye@github.com>2023-11-14 19:53:55 (GMT)
committerJunio C Hamano <gitster@pobox.com>2023-11-16 05:03:00 (GMT)
commitbd98f9774e10808276c13f8438aa3c71093bc6a4 (patch)
treebfb967ae17de37af5431ecc58a32faea161b61b1 /ref-filter.c
parent613d9915499ed9d1fd21d743002d17ef972e9e57 (diff)
downloadgit-bd98f9774e10808276c13f8438aa3c71093bc6a4.zip
git-bd98f9774e10808276c13f8438aa3c71093bc6a4.tar.gz
git-bd98f9774e10808276c13f8438aa3c71093bc6a4.tar.bz2
ref-filter.c: filter & format refs in the same callback
Update 'filter_and_format_refs()' to try to perform ref filtering & formatting in a single ref iteration, without an intermediate 'struct ref_array'. This can only be done if no operations need to be performed on a pre-filtered array; specifically, if the refs are - filtered on reachability, - sorted, or - formatted with ahead-behind information they cannot be filtered & formatted in the same iteration. In that case, fall back on the current filter-then-sort-then-format flow. This optimization substantially improves memory usage due to no longer storing a ref array in memory. In some cases, it also dramatically reduces runtime (e.g. 'git for-each-ref --no-sort --count=1', which no longer loads all refs into a 'struct ref_array' to printing only the first ref). Signed-off-by: Victoria Dye <vdye@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'ref-filter.c')
-rw-r--r--ref-filter.c88
1 files changed, 82 insertions, 6 deletions
diff --git a/ref-filter.c b/ref-filter.c
index 8bfdc66..ca4ebed 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2779,6 +2779,49 @@ static void free_array_item(struct ref_array_item *item)
free(item);
}
+struct ref_filter_and_format_cbdata {
+ struct ref_filter *filter;
+ struct ref_format *format;
+
+ struct ref_filter_and_format_internal {
+ int count;
+ } internal;
+};
+
+static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+ struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
+ struct ref_array_item *ref;
+ struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+ ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+ if (!ref)
+ return 0;
+
+ if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+ die("%s", err.buf);
+
+ if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
+ fwrite(output.buf, 1, output.len, stdout);
+ putchar('\n');
+ }
+
+ strbuf_release(&output);
+ strbuf_release(&err);
+ free_array_item(ref);
+
+ /*
+ * Increment the running count of refs that match the filter. If
+ * max_count is set and we've reached the max, stop the ref
+ * iteration by returning a nonzero value.
+ */
+ if (ref_cbdata->format->array_opts.max_count &&
+ ++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count)
+ return 1;
+
+ return 0;
+}
+
/* Free all memory allocated for ref_array */
void ref_array_clear(struct ref_array *array)
{
@@ -2962,16 +3005,49 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
return ret;
}
+static inline int can_do_iterative_format(struct ref_filter *filter,
+ struct ref_sorting *sorting,
+ struct ref_format *format)
+{
+ /*
+ * Filtering & formatting results within a single ref iteration
+ * callback is not compatible with options that require
+ * post-processing a filtered ref_array. These include:
+ * - filtering on reachability
+ * - sorting the filtered results
+ * - including ahead-behind information in the formatted output
+ */
+ return !(filter->reachable_from ||
+ filter->unreachable_from ||
+ sorting ||
+ format->bases.nr);
+}
+
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);
+ if (can_do_iterative_format(filter, sorting, format)) {
+ int save_commit_buffer_orig;
+ struct ref_filter_and_format_cbdata ref_cbdata = {
+ .filter = filter,
+ .format = format,
+ };
+
+ save_commit_buffer_orig = save_commit_buffer;
+ save_commit_buffer = 0;
+
+ do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata);
+
+ save_commit_buffer = save_commit_buffer_orig;
+ } else {
+ 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)