Skip to content

Commit 8849ac4

Browse files
rscharfedscho
authored andcommitted
ref-filter: move ahead-behind bases into used_atom
verify_ref_format() parses a ref-filter format string and stores recognized items in the static array "used_atom". For "ahead-behind:<committish>" it stores the committish part in a string_list member "bases" of struct ref_format. ref_sorting_options() also parses bare ref-filter format items and stores stores recognized ones in "used_atom" as well. The committish parts go to a dummy struct ref_format in parse_sorting_atom(), though, and are leaked and forgotten. If verify_ref_format() is called before ref_sorting_options(), like in git for-each-ref, then all works well if the sort key is included in the format string. If it isn't then sorting cannot work as the committishes are missing. If ref_sorting_options() is called first, like in git branch, then we have the additional issue that if the sort key is included in the format string then filter_ahead_behind() can't see its committish, will not generate any results for it and thus it will be expanded to an empty string. Fix those issues by replacing the string_list with a field in used_atom for storing the committish. This way it can be shared for handling both ref-filter format strings and sorting options in the same command. Reported-by: Ross Goldberg <ross.goldberg@gmail.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 5cc2d6c commit 8849ac4

File tree

4 files changed

+59
-26
lines changed

4 files changed

+59
-26
lines changed

builtin/branch.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
473473
if (verify_ref_format(format))
474474
die(_("unable to parse format string"));
475475

476-
filter_ahead_behind(the_repository, format, &array);
476+
filter_ahead_behind(the_repository, &array);
477477
ref_array_sort(sorting, &array);
478478

479479
if (column_active(colopts)) {

ref-filter.c

+30-20
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ static struct used_atom {
235235
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
236236
S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
237237
} signature;
238+
struct {
239+
struct commit *commit;
240+
} base;
238241
struct strvec describe_args;
239242
struct refname_atom refname;
240243
char *head;
@@ -891,18 +894,15 @@ static int rest_atom_parser(struct ref_format *format UNUSED,
891894
return 0;
892895
}
893896

894-
static int ahead_behind_atom_parser(struct ref_format *format,
895-
struct used_atom *atom UNUSED,
897+
static int ahead_behind_atom_parser(struct ref_format *format UNUSED,
898+
struct used_atom *atom,
896899
const char *arg, struct strbuf *err)
897900
{
898-
struct string_list_item *item;
899-
900901
if (!arg)
901902
return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));
902903

903-
item = string_list_append(&format->bases, arg);
904-
item->util = lookup_commit_reference_by_name(arg);
905-
if (!item->util)
904+
atom->u.base.commit = lookup_commit_reference_by_name(arg);
905+
if (!atom->u.base.commit)
906906
die("failed to find '%s'", arg);
907907

908908
return 0;
@@ -3084,22 +3084,30 @@ static void reach_filter(struct ref_array *array,
30843084
}
30853085

30863086
void filter_ahead_behind(struct repository *r,
3087-
struct ref_format *format,
30883087
struct ref_array *array)
30893088
{
30903089
struct commit **commits;
3091-
size_t commits_nr = format->bases.nr + array->nr;
3090+
size_t bases_nr, commits_nr;
30923091

3093-
if (!format->bases.nr || !array->nr)
3092+
if (!array->nr)
30943093
return;
30953094

3096-
ALLOC_ARRAY(commits, commits_nr);
3097-
for (size_t i = 0; i < format->bases.nr; i++)
3098-
commits[i] = format->bases.items[i].util;
3095+
for (size_t i = bases_nr = 0; i < used_atom_cnt; i++) {
3096+
if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
3097+
bases_nr++;
3098+
}
3099+
if (!bases_nr)
3100+
return;
30993101

3100-
ALLOC_ARRAY(array->counts, st_mult(format->bases.nr, array->nr));
3102+
ALLOC_ARRAY(commits, st_add(bases_nr, array->nr));
3103+
for (size_t i = 0, j = 0; i < used_atom_cnt; i++) {
3104+
if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
3105+
commits[j++] = used_atom[i].u.base.commit;
3106+
}
31013107

3102-
commits_nr = format->bases.nr;
3108+
ALLOC_ARRAY(array->counts, st_mult(bases_nr, array->nr));
3109+
3110+
commits_nr = bases_nr;
31033111
array->counts_nr = 0;
31043112
for (size_t i = 0; i < array->nr; i++) {
31053113
const char *name = array->items[i]->refname;
@@ -3108,8 +3116,8 @@ void filter_ahead_behind(struct repository *r,
31083116
if (!commits[commits_nr])
31093117
continue;
31103118

3111-
CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
3112-
for (size_t j = 0; j < format->bases.nr; j++) {
3119+
CALLOC_ARRAY(array->items[i]->counts, bases_nr);
3120+
for (size_t j = 0; j < bases_nr; j++) {
31133121
struct ahead_behind_count *count;
31143122
count = &array->counts[array->counts_nr++];
31153123
count->tip_index = commits_nr;
@@ -3277,9 +3285,12 @@ static inline int can_do_iterative_format(struct ref_filter *filter,
32773285
* - filtering on reachability
32783286
* - including ahead-behind information in the formatted output
32793287
*/
3288+
for (size_t i = 0; i < used_atom_cnt; i++) {
3289+
if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
3290+
return 0;
3291+
}
32803292
return !(filter->reachable_from ||
32813293
filter->unreachable_from ||
3282-
format->bases.nr ||
32833294
format->is_base_tips.nr);
32843295
}
32853296

@@ -3303,7 +3314,7 @@ void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
33033314
} else {
33043315
struct ref_array array = { 0 };
33053316
filter_refs(&array, filter, type);
3306-
filter_ahead_behind(the_repository, format, &array);
3317+
filter_ahead_behind(the_repository, &array);
33073318
filter_is_base(the_repository, format, &array);
33083319
ref_array_sort(sorting, &array);
33093320
print_formatted_ref_array(&array, format);
@@ -3647,7 +3658,6 @@ void ref_format_init(struct ref_format *format)
36473658

36483659
void ref_format_clear(struct ref_format *format)
36493660
{
3650-
string_list_clear(&format->bases, 0);
36513661
string_list_clear(&format->is_base_tips, 0);
36523662
ref_format_init(format);
36533663
}

ref-filter.h

-5
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ struct ref_format {
9999
/* Internal state to ref-filter */
100100
int need_color_reset_at_eol;
101101

102-
/* List of bases for ahead-behind counts. */
103-
struct string_list bases;
104-
105102
/* List of bases for is-base indicators. */
106103
struct string_list is_base_tips;
107104

@@ -117,7 +114,6 @@ struct ref_format {
117114
}
118115
#define REF_FORMAT_INIT { \
119116
.use_color = -1, \
120-
.bases = STRING_LIST_INIT_DUP, \
121117
.is_base_tips = STRING_LIST_INIT_DUP, \
122118
}
123119

@@ -205,7 +201,6 @@ struct ref_array_item *ref_array_push(struct ref_array *array,
205201
* If this is not called, then any ahead-behind atoms will be blank.
206202
*/
207203
void filter_ahead_behind(struct repository *r,
208-
struct ref_format *format,
209204
struct ref_array *array);
210205

211206
/*

t/t3203-branch-output.sh

+28
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,34 @@ test_expect_success 'git branch --format with ahead-behind' '
368368
test_cmp expect actual
369369
'
370370

371+
test_expect_success 'git branch `--sort=[-]ahead-behind` option' '
372+
cat >expect <<-\EOF &&
373+
(HEAD detached from fromtag) 0 0
374+
refs/heads/ambiguous 0 0
375+
refs/heads/branch-two 0 0
376+
refs/heads/branch-one 1 0
377+
refs/heads/main 1 0
378+
refs/heads/ref-to-branch 1 0
379+
refs/heads/ref-to-remote 1 0
380+
EOF
381+
git branch --format="%(refname) %(ahead-behind:HEAD)" \
382+
--sort=refname --sort=ahead-behind:HEAD >actual &&
383+
test_cmp expect actual &&
384+
385+
cat >expect <<-\EOF &&
386+
(HEAD detached from fromtag) 0 0
387+
refs/heads/branch-one 1 0
388+
refs/heads/main 1 0
389+
refs/heads/ref-to-branch 1 0
390+
refs/heads/ref-to-remote 1 0
391+
refs/heads/ambiguous 0 0
392+
refs/heads/branch-two 0 0
393+
EOF
394+
git branch --format="%(refname) %(ahead-behind:HEAD)" \
395+
--sort=refname --sort=-ahead-behind:HEAD >actual &&
396+
test_cmp expect actual
397+
'
398+
371399
test_expect_success 'git branch with --format=%(rest) must fail' '
372400
test_must_fail git branch --format="%(rest)" >actual
373401
'

0 commit comments

Comments
 (0)