Skip to content

Commit 7727da9

Browse files
committed
Merge branch 'ds/ahead-behind'
"git for-each-ref" learns '%(ahead-behind:<base>)' that computes the distances from a single reference point in the history with bunch of commits in bulk. * ds/ahead-behind: commit-reach: add tips_reachable_from_bases() for-each-ref: add ahead-behind format atom commit-reach: implement ahead_behind() logic commit-graph: introduce `ensure_generations_valid()` commit-graph: return generation from memory commit-graph: simplify compute_generation_numbers() commit-graph: refactor compute_topological_levels() for-each-ref: explicitly test no matches for-each-ref: add --stdin option
2 parents ae73b2c + cbfe360 commit 7727da9

17 files changed

+866
-91
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ SYNOPSIS
99
--------
1010
[verse]
1111
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
12-
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
12+
[(--sort=<key>)...] [--format=<format>]
13+
[ --stdin | <pattern>... ]
1314
[--points-at=<object>]
1415
[--merged[=<object>]] [--no-merged[=<object>]]
1516
[--contains[=<object>]] [--no-contains[=<object>]]
@@ -32,6 +33,10 @@ OPTIONS
3233
literally, in the latter case matching completely or from the
3334
beginning up to a slash.
3435

36+
--stdin::
37+
If `--stdin` is supplied, then the list of patterns is read from
38+
standard input instead of from the argument list.
39+
3540
--count=<count>::
3641
By default the command shows all refs that match
3742
`<pattern>`. This option makes it stop after showing
@@ -217,6 +222,11 @@ worktreepath::
217222
out, if it is checked out in any linked worktree. Empty string
218223
otherwise.
219224

225+
ahead-behind:<committish>::
226+
Two integers, separated by a space, demonstrating the number of
227+
commits ahead and behind, respectively, when comparing the output
228+
ref to the `<committish>` specified in the format.
229+
220230
In addition to the above, for commit and tag objects, the header
221231
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
222232
be used to specify the value in the header field.

builtin/branch.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
448448
if (verify_ref_format(format))
449449
die(_("unable to parse format string"));
450450

451+
filter_ahead_behind(the_repository, format, &array);
451452
ref_array_sort(sorting, &array);
452453

453454
for (i = 0; i < array.nr; i++) {

builtin/for-each-ref.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "object.h"
66
#include "parse-options.h"
77
#include "ref-filter.h"
8+
#include "strvec.h"
9+
#include "commit-reach.h"
810

911
static char const * const for_each_ref_usage[] = {
1012
N_("git for-each-ref [<options>] [<pattern>]"),
@@ -25,6 +27,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
2527
struct ref_format format = REF_FORMAT_INIT;
2628
struct strbuf output = STRBUF_INIT;
2729
struct strbuf err = STRBUF_INIT;
30+
int from_stdin = 0;
31+
struct strvec vec = STRVEC_INIT;
2832

2933
struct option opts[] = {
3034
OPT_BIT('s', "shell", &format.quote_style,
@@ -49,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
4953
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
5054
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
5155
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
56+
OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
5257
OPT_END(),
5358
};
5459

@@ -75,9 +80,27 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
7580
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
7681
filter.ignore_case = icase;
7782

78-
filter.name_patterns = argv;
83+
if (from_stdin) {
84+
struct strbuf line = STRBUF_INIT;
85+
86+
if (argv[0])
87+
die(_("unknown arguments supplied with --stdin"));
88+
89+
while (strbuf_getline(&line, stdin) != EOF)
90+
strvec_push(&vec, line.buf);
91+
92+
strbuf_release(&line);
93+
94+
/* vec.v is NULL-terminated, just like 'argv'. */
95+
filter.name_patterns = vec.v;
96+
} else {
97+
filter.name_patterns = argv;
98+
}
99+
79100
filter.match_as_path = 1;
80101
filter_refs(&array, &filter, FILTER_REFS_ALL);
102+
filter_ahead_behind(the_repository, &format, &array);
103+
81104
ref_array_sort(sorting, &array);
82105

83106
if (!maxcount || array.nr < maxcount)
@@ -97,5 +120,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
97120
free_commit_list(filter.with_commit);
98121
free_commit_list(filter.no_commit);
99122
ref_sorting_release(sorting);
123+
strvec_clear(&vec);
100124
return 0;
101125
}

builtin/tag.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
6767
die(_("unable to parse format string"));
6868
filter->with_commit_tag_algo = 1;
6969
filter_refs(&array, filter, FILTER_REFS_TAGS);
70+
filter_ahead_behind(the_repository, format, &array);
7071
ref_array_sort(sorting, &array);
7172

7273
for (i = 0; i < array.nr; i++) {

commit-graph.c

Lines changed: 136 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,10 @@ timestamp_t commit_graph_generation(const struct commit *c)
117117
struct commit_graph_data *data =
118118
commit_graph_data_slab_peek(&commit_graph_data_slab, c);
119119

120-
if (!data)
121-
return GENERATION_NUMBER_INFINITY;
122-
else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
123-
return GENERATION_NUMBER_INFINITY;
120+
if (data && data->generation)
121+
return data->generation;
124122

125-
return data->generation;
123+
return GENERATION_NUMBER_INFINITY;
126124
}
127125

128126
static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
@@ -1447,66 +1445,144 @@ static void close_reachable(struct write_commit_graph_context *ctx)
14471445
stop_progress(&ctx->progress);
14481446
}
14491447

1450-
static void compute_topological_levels(struct write_commit_graph_context *ctx)
1448+
struct compute_generation_info {
1449+
struct repository *r;
1450+
struct packed_commit_list *commits;
1451+
struct progress *progress;
1452+
int progress_cnt;
1453+
1454+
timestamp_t (*get_generation)(struct commit *c, void *data);
1455+
void (*set_generation)(struct commit *c, timestamp_t gen, void *data);
1456+
void *data;
1457+
};
1458+
1459+
static timestamp_t compute_generation_from_max(struct commit *c,
1460+
timestamp_t max_gen,
1461+
int generation_version)
1462+
{
1463+
switch (generation_version) {
1464+
case 1: /* topological levels */
1465+
if (max_gen > GENERATION_NUMBER_V1_MAX - 1)
1466+
max_gen = GENERATION_NUMBER_V1_MAX - 1;
1467+
return max_gen + 1;
1468+
1469+
case 2: /* corrected commit date */
1470+
if (c->date && c->date > max_gen)
1471+
max_gen = c->date - 1;
1472+
return max_gen + 1;
1473+
1474+
default:
1475+
BUG("attempting unimplemented version");
1476+
}
1477+
}
1478+
1479+
static void compute_reachable_generation_numbers(
1480+
struct compute_generation_info *info,
1481+
int generation_version)
14511482
{
14521483
int i;
14531484
struct commit_list *list = NULL;
14541485

1455-
if (ctx->report_progress)
1456-
ctx->progress = start_delayed_progress(
1457-
_("Computing commit graph topological levels"),
1458-
ctx->commits.nr);
1459-
for (i = 0; i < ctx->commits.nr; i++) {
1460-
struct commit *c = ctx->commits.list[i];
1461-
uint32_t level;
1462-
1463-
repo_parse_commit(ctx->r, c);
1464-
level = *topo_level_slab_at(ctx->topo_levels, c);
1486+
for (i = 0; i < info->commits->nr; i++) {
1487+
struct commit *c = info->commits->list[i];
1488+
timestamp_t gen;
1489+
repo_parse_commit(info->r, c);
1490+
gen = info->get_generation(c, info->data);
1491+
display_progress(info->progress, info->progress_cnt + 1);
14651492

1466-
display_progress(ctx->progress, i + 1);
1467-
if (level != GENERATION_NUMBER_ZERO)
1493+
if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
14681494
continue;
14691495

14701496
commit_list_insert(c, &list);
14711497
while (list) {
14721498
struct commit *current = list->item;
14731499
struct commit_list *parent;
14741500
int all_parents_computed = 1;
1475-
uint32_t max_level = 0;
1501+
uint32_t max_gen = 0;
14761502

14771503
for (parent = current->parents; parent; parent = parent->next) {
1478-
repo_parse_commit(ctx->r, parent->item);
1479-
level = *topo_level_slab_at(ctx->topo_levels, parent->item);
1504+
repo_parse_commit(info->r, parent->item);
1505+
gen = info->get_generation(parent->item, info->data);
14801506

1481-
if (level == GENERATION_NUMBER_ZERO) {
1507+
if (gen == GENERATION_NUMBER_ZERO) {
14821508
all_parents_computed = 0;
14831509
commit_list_insert(parent->item, &list);
14841510
break;
14851511
}
14861512

1487-
if (level > max_level)
1488-
max_level = level;
1513+
if (gen > max_gen)
1514+
max_gen = gen;
14891515
}
14901516

14911517
if (all_parents_computed) {
14921518
pop_commit(&list);
1493-
1494-
if (max_level > GENERATION_NUMBER_V1_MAX - 1)
1495-
max_level = GENERATION_NUMBER_V1_MAX - 1;
1496-
*topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
1519+
gen = compute_generation_from_max(
1520+
current, max_gen,
1521+
generation_version);
1522+
info->set_generation(current, gen, info->data);
14971523
}
14981524
}
14991525
}
1526+
}
1527+
1528+
static timestamp_t get_topo_level(struct commit *c, void *data)
1529+
{
1530+
struct write_commit_graph_context *ctx = data;
1531+
return *topo_level_slab_at(ctx->topo_levels, c);
1532+
}
1533+
1534+
static void set_topo_level(struct commit *c, timestamp_t t, void *data)
1535+
{
1536+
struct write_commit_graph_context *ctx = data;
1537+
*topo_level_slab_at(ctx->topo_levels, c) = (uint32_t)t;
1538+
}
1539+
1540+
static void compute_topological_levels(struct write_commit_graph_context *ctx)
1541+
{
1542+
struct compute_generation_info info = {
1543+
.r = ctx->r,
1544+
.commits = &ctx->commits,
1545+
.get_generation = get_topo_level,
1546+
.set_generation = set_topo_level,
1547+
.data = ctx,
1548+
};
1549+
1550+
if (ctx->report_progress)
1551+
info.progress = ctx->progress
1552+
= start_delayed_progress(
1553+
_("Computing commit graph topological levels"),
1554+
ctx->commits.nr);
1555+
1556+
compute_reachable_generation_numbers(&info, 1);
1557+
15001558
stop_progress(&ctx->progress);
15011559
}
15021560

1561+
static timestamp_t get_generation_from_graph_data(struct commit *c, void *data)
1562+
{
1563+
return commit_graph_data_at(c)->generation;
1564+
}
1565+
1566+
static void set_generation_v2(struct commit *c, timestamp_t t, void *data)
1567+
{
1568+
struct commit_graph_data *g = commit_graph_data_at(c);
1569+
g->generation = (uint32_t)t;
1570+
}
1571+
15031572
static void compute_generation_numbers(struct write_commit_graph_context *ctx)
15041573
{
15051574
int i;
1506-
struct commit_list *list = NULL;
1575+
struct compute_generation_info info = {
1576+
.r = ctx->r,
1577+
.commits = &ctx->commits,
1578+
.get_generation = get_generation_from_graph_data,
1579+
.set_generation = set_generation_v2,
1580+
.data = ctx,
1581+
};
15071582

15081583
if (ctx->report_progress)
1509-
ctx->progress = start_delayed_progress(
1584+
info.progress = ctx->progress
1585+
= start_delayed_progress(
15101586
_("Computing commit graph generation numbers"),
15111587
ctx->commits.nr);
15121588

@@ -1518,47 +1594,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
15181594
}
15191595
}
15201596

1521-
for (i = 0; i < ctx->commits.nr; i++) {
1522-
struct commit *c = ctx->commits.list[i];
1523-
timestamp_t corrected_commit_date;
1524-
1525-
repo_parse_commit(ctx->r, c);
1526-
corrected_commit_date = commit_graph_data_at(c)->generation;
1527-
1528-
display_progress(ctx->progress, i + 1);
1529-
if (corrected_commit_date != GENERATION_NUMBER_ZERO)
1530-
continue;
1531-
1532-
commit_list_insert(c, &list);
1533-
while (list) {
1534-
struct commit *current = list->item;
1535-
struct commit_list *parent;
1536-
int all_parents_computed = 1;
1537-
timestamp_t max_corrected_commit_date = 0;
1538-
1539-
for (parent = current->parents; parent; parent = parent->next) {
1540-
repo_parse_commit(ctx->r, parent->item);
1541-
corrected_commit_date = commit_graph_data_at(parent->item)->generation;
1542-
1543-
if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
1544-
all_parents_computed = 0;
1545-
commit_list_insert(parent->item, &list);
1546-
break;
1547-
}
1548-
1549-
if (corrected_commit_date > max_corrected_commit_date)
1550-
max_corrected_commit_date = corrected_commit_date;
1551-
}
1552-
1553-
if (all_parents_computed) {
1554-
pop_commit(&list);
1555-
1556-
if (current->date && current->date > max_corrected_commit_date)
1557-
max_corrected_commit_date = current->date - 1;
1558-
commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
1559-
}
1560-
}
1561-
}
1597+
compute_reachable_generation_numbers(&info, 2);
15621598

15631599
for (i = 0; i < ctx->commits.nr; i++) {
15641600
struct commit *c = ctx->commits.list[i];
@@ -1569,6 +1605,35 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
15691605
stop_progress(&ctx->progress);
15701606
}
15711607

1608+
static void set_generation_in_graph_data(struct commit *c, timestamp_t t,
1609+
void *data)
1610+
{
1611+
commit_graph_data_at(c)->generation = t;
1612+
}
1613+
1614+
/*
1615+
* After this method, all commits reachable from those in the given
1616+
* list will have non-zero, non-infinite generation numbers.
1617+
*/
1618+
void ensure_generations_valid(struct repository *r,
1619+
struct commit **commits, size_t nr)
1620+
{
1621+
int generation_version = get_configured_generation_version(r);
1622+
struct packed_commit_list list = {
1623+
.list = commits,
1624+
.alloc = nr,
1625+
.nr = nr,
1626+
};
1627+
struct compute_generation_info info = {
1628+
.r = r,
1629+
.commits = &list,
1630+
.get_generation = get_generation_from_graph_data,
1631+
.set_generation = set_generation_in_graph_data,
1632+
};
1633+
1634+
compute_reachable_generation_numbers(&info, generation_version);
1635+
}
1636+
15721637
static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
15731638
{
15741639
trace2_data_intmax("commit-graph", ctx->r, "filter-computed",

commit-graph.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,12 @@ struct commit_graph_data {
189189
*/
190190
timestamp_t commit_graph_generation(const struct commit *);
191191
uint32_t commit_graph_position(const struct commit *);
192+
193+
/*
194+
* After this method, all commits reachable from those in the given
195+
* list will have non-zero, non-infinite generation numbers.
196+
*/
197+
void ensure_generations_valid(struct repository *r,
198+
struct commit **commits, size_t nr);
199+
192200
#endif

0 commit comments

Comments
 (0)