Skip to content

Commit bd98f97

Browse files
vdyegitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 613d991 commit bd98f97

File tree

1 file changed

+82
-6
lines changed

1 file changed

+82
-6
lines changed

ref-filter.c

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,6 +2779,49 @@ static void free_array_item(struct ref_array_item *item)
27792779
free(item);
27802780
}
27812781

2782+
struct ref_filter_and_format_cbdata {
2783+
struct ref_filter *filter;
2784+
struct ref_format *format;
2785+
2786+
struct ref_filter_and_format_internal {
2787+
int count;
2788+
} internal;
2789+
};
2790+
2791+
static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
2792+
{
2793+
struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
2794+
struct ref_array_item *ref;
2795+
struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
2796+
2797+
ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
2798+
if (!ref)
2799+
return 0;
2800+
2801+
if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
2802+
die("%s", err.buf);
2803+
2804+
if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
2805+
fwrite(output.buf, 1, output.len, stdout);
2806+
putchar('\n');
2807+
}
2808+
2809+
strbuf_release(&output);
2810+
strbuf_release(&err);
2811+
free_array_item(ref);
2812+
2813+
/*
2814+
* Increment the running count of refs that match the filter. If
2815+
* max_count is set and we've reached the max, stop the ref
2816+
* iteration by returning a nonzero value.
2817+
*/
2818+
if (ref_cbdata->format->array_opts.max_count &&
2819+
++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count)
2820+
return 1;
2821+
2822+
return 0;
2823+
}
2824+
27822825
/* Free all memory allocated for ref_array */
27832826
void ref_array_clear(struct ref_array *array)
27842827
{
@@ -2962,16 +3005,49 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
29623005
return ret;
29633006
}
29643007

3008+
static inline int can_do_iterative_format(struct ref_filter *filter,
3009+
struct ref_sorting *sorting,
3010+
struct ref_format *format)
3011+
{
3012+
/*
3013+
* Filtering & formatting results within a single ref iteration
3014+
* callback is not compatible with options that require
3015+
* post-processing a filtered ref_array. These include:
3016+
* - filtering on reachability
3017+
* - sorting the filtered results
3018+
* - including ahead-behind information in the formatted output
3019+
*/
3020+
return !(filter->reachable_from ||
3021+
filter->unreachable_from ||
3022+
sorting ||
3023+
format->bases.nr);
3024+
}
3025+
29653026
void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
29663027
struct ref_sorting *sorting,
29673028
struct ref_format *format)
29683029
{
2969-
struct ref_array array = { 0 };
2970-
filter_refs(&array, filter, type);
2971-
filter_ahead_behind(the_repository, format, &array);
2972-
ref_array_sort(sorting, &array);
2973-
print_formatted_ref_array(&array, format);
2974-
ref_array_clear(&array);
3030+
if (can_do_iterative_format(filter, sorting, format)) {
3031+
int save_commit_buffer_orig;
3032+
struct ref_filter_and_format_cbdata ref_cbdata = {
3033+
.filter = filter,
3034+
.format = format,
3035+
};
3036+
3037+
save_commit_buffer_orig = save_commit_buffer;
3038+
save_commit_buffer = 0;
3039+
3040+
do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata);
3041+
3042+
save_commit_buffer = save_commit_buffer_orig;
3043+
} else {
3044+
struct ref_array array = { 0 };
3045+
filter_refs(&array, filter, type);
3046+
filter_ahead_behind(the_repository, format, &array);
3047+
ref_array_sort(sorting, &array);
3048+
print_formatted_ref_array(&array, format);
3049+
ref_array_clear(&array);
3050+
}
29753051
}
29763052

29773053
static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)

0 commit comments

Comments
 (0)