list-objects-filter-options: avoid strbuf_split_str()

parse_combine_filter() splits a combine: filter spec at '+' using
strbuf_split_str(), which yields an array of strbufs with the
delimiter left at the end of each non-final piece.  The code then
mutates each non-final piece to strip the trailing '+' before parsing.

Allocating an array of strbufs is unnecessary.  The function processes
one sub-spec at a time and does not use strbuf editing on the pieces.
The two helpers it calls, has_reserved_character() and
parse_combine_subfilter(), only read the string content of the strbuf
they receive.

Walk the input string directly with strchrnul() to find each '+',
copying each sub-spec into a reusable temporary buffer.  The '+'
delimiter is naturally excluded.  Empty sub-specs (e.g. from a
trailing '+') are silently skipped for consistency.  Change the
helpers to take const char * instead of struct strbuf *.

The test that expected an error on a trailing '+' is removed, since
that behavior was incorrect.

Signed-off-by: Deveshi Dwivedi <deveshigurgaon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Deveshi Dwivedi 2026-03-11 17:33:36 +00:00 committed by Junio C Hamano
parent 4107c0bb34
commit f21967e541
2 changed files with 20 additions and 24 deletions

View File

@ -125,9 +125,9 @@ int gently_parse_list_objects_filter(
static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?"; static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?";


static int has_reserved_character( static int has_reserved_character(
struct strbuf *sub_spec, struct strbuf *errbuf) const char *sub_spec, struct strbuf *errbuf)
{ {
const char *c = sub_spec->buf; const char *c = sub_spec;
while (*c) { while (*c) {
if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) { if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) {
strbuf_addf( strbuf_addf(
@ -144,7 +144,7 @@ static int has_reserved_character(


static int parse_combine_subfilter( static int parse_combine_subfilter(
struct list_objects_filter_options *filter_options, struct list_objects_filter_options *filter_options,
struct strbuf *subspec, const char *subspec,
struct strbuf *errbuf) struct strbuf *errbuf)
{ {
size_t new_index = filter_options->sub_nr; size_t new_index = filter_options->sub_nr;
@ -155,7 +155,7 @@ static int parse_combine_subfilter(
filter_options->sub_alloc); filter_options->sub_alloc);
list_objects_filter_init(&filter_options->sub[new_index]); list_objects_filter_init(&filter_options->sub[new_index]);


decoded = url_percent_decode(subspec->buf); decoded = url_percent_decode(subspec);


result = has_reserved_character(subspec, errbuf); result = has_reserved_character(subspec, errbuf);
if (result) if (result)
@ -182,34 +182,34 @@ static int parse_combine_filter(
const char *arg, const char *arg,
struct strbuf *errbuf) struct strbuf *errbuf)
{ {
struct strbuf **subspecs = strbuf_split_str(arg, '+', 0); const char *p = arg;
size_t sub; struct strbuf sub = STRBUF_INIT;
int result = 0; int result = 0;


if (!subspecs[0]) { if (!*p) {
strbuf_addstr(errbuf, _("expected something after combine:")); strbuf_addstr(errbuf, _("expected something after combine:"));
result = 1; result = 1;
goto cleanup; goto cleanup;
} }


for (sub = 0; subspecs[sub] && !result; sub++) { while (*p && !result) {
if (subspecs[sub + 1]) { const char *end = strchrnul(p, '+');
/*
* This is not the last subspec. Remove trailing "+" so strbuf_reset(&sub);
* we can parse it. strbuf_add(&sub, p, end - p);
*/
size_t last = subspecs[sub]->len - 1; if (sub.len)
assert(subspecs[sub]->buf[last] == '+'); result = parse_combine_subfilter(filter_options, sub.buf, errbuf);
strbuf_remove(subspecs[sub], last, 1);
} if (!*end)
result = parse_combine_subfilter( break;
filter_options, subspecs[sub], errbuf); p = end + 1;
} }
strbuf_release(&sub);


filter_options->choice = LOFC_COMBINE; filter_options->choice = LOFC_COMBINE;


cleanup: cleanup:
strbuf_list_free(subspecs);
if (result) if (result)
list_objects_filter_release(filter_options); list_objects_filter_release(filter_options);
return result; return result;

View File

@ -483,10 +483,6 @@ test_expect_success 'combine:... with non-encoded reserved chars' '
"must escape char in sub-filter-spec: .\~." "must escape char in sub-filter-spec: .\~."
' '


test_expect_success 'validate err msg for "combine:<valid-filter>+"' '
expect_invalid_filter_spec combine:tree:2+ "expected .tree:<depth>."
'

test_expect_success 'combine:... with edge-case hex digits: Ff Aa 0 9' ' test_expect_success 'combine:... with edge-case hex digits: Ff Aa 0 9' '
git -C r3 rev-list --objects --filter="combine:tree:2+bl%6Fb:n%6fne" \ git -C r3 rev-list --objects --filter="combine:tree:2+bl%6Fb:n%6fne" \
HEAD >actual && HEAD >actual &&