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>maint
parent
613d991549
commit
bd98f9774e
88
ref-filter.c
88
ref-filter.c
|
@ -2779,6 +2779,49 @@ static void free_array_item(struct ref_array_item *item)
|
||||||
free(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 */
|
/* Free all memory allocated for ref_array */
|
||||||
void ref_array_clear(struct ref_array *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;
|
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,
|
void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
|
||||||
struct ref_sorting *sorting,
|
struct ref_sorting *sorting,
|
||||||
struct ref_format *format)
|
struct ref_format *format)
|
||||||
{
|
{
|
||||||
struct ref_array array = { 0 };
|
if (can_do_iterative_format(filter, sorting, format)) {
|
||||||
filter_refs(&array, filter, type);
|
int save_commit_buffer_orig;
|
||||||
filter_ahead_behind(the_repository, format, &array);
|
struct ref_filter_and_format_cbdata ref_cbdata = {
|
||||||
ref_array_sort(sorting, &array);
|
.filter = filter,
|
||||||
print_formatted_ref_array(&array, format);
|
.format = format,
|
||||||
ref_array_clear(&array);
|
};
|
||||||
|
|
||||||
|
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)
|
static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
|
||||||
|
|
Loading…
Reference in New Issue