Skip to content

Commit 0b08ba7

Browse files
committed
Merge branch 'en/ancestry-path-in-a-range'
"git rev-list --ancestry-path=C A..B" is a natural extension of "git rev-list A..B"; instead of choosing a subset of A..B to those that have ancestry relationship with A, it lets a subset with ancestry relationship with C. * en/ancestry-path-in-a-range: revision: allow --ancestry-path to take an argument t6019: modernize tests with helper rev-list-options.txt: fix simple typo
2 parents 64cb4c3 + 257418c commit 0b08ba7

File tree

5 files changed

+141
-106
lines changed

5 files changed

+141
-106
lines changed

Documentation/rev-list-options.txt

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,14 @@ Default mode::
392392
merges from the resulting history, as there are no selected
393393
commits contributing to this merge.
394394

395-
--ancestry-path::
395+
--ancestry-path[=<commit>]::
396396
When given a range of commits to display (e.g. 'commit1..commit2'
397-
or 'commit2 {caret}commit1'), only display commits that exist
398-
directly on the ancestry chain between the 'commit1' and
399-
'commit2', i.e. commits that are both descendants of 'commit1',
400-
and ancestors of 'commit2'.
397+
or 'commit2 {caret}commit1'), only display commits in that range
398+
that are ancestors of <commit>, descendants of <commit>, or
399+
<commit> itself. If no commit is specified, use 'commit1' (the
400+
excluded part of the range) as <commit>. Can be passed multiple
401+
times; if so, a commit is included if it is any of the commits
402+
given or if it is an ancestor or descendant of one of them.
401403

402404
A more detailed explanation follows.
403405

@@ -571,11 +573,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`:
571573

572574
There is another simplification mode available:
573575

574-
--ancestry-path::
575-
Limit the displayed commits to those directly on the ancestry
576-
chain between the ``from'' and ``to'' commits in the given commit
577-
range. I.e. only display commits that are ancestor of the ``to''
578-
commit and descendants of the ``from'' commit.
576+
--ancestry-path[=<commit>]::
577+
Limit the displayed commits to those which are an ancestor of
578+
<commit>, or which are a descendant of <commit>, or are <commit>
579+
itself.
579580
+
580581
As an example use case, consider the following commit history:
581582
+
@@ -607,6 +608,29 @@ option does. Applied to the 'D..M' range, it results in:
607608
\
608609
L--M
609610
-----------------------------------------------------------------------
611+
+
612+
We can also use `--ancestry-path=D` instead of `--ancestry-path` which
613+
means the same thing when applied to the 'D..M' range but is just more
614+
explicit.
615+
+
616+
If we instead are interested in a given topic within this range, and all
617+
commits affected by that topic, we may only want to view the subset of
618+
`D..M` which contain that topic in their ancestry path. So, using
619+
`--ancestry-path=H D..M` for example would result in:
620+
+
621+
-----------------------------------------------------------------------
622+
E
623+
\
624+
G---H---I---J
625+
\
626+
L--M
627+
-----------------------------------------------------------------------
628+
+
629+
Whereas `--ancestry-path=K D..M` would result in
630+
+
631+
-----------------------------------------------------------------------
632+
K---------------L--M
633+
-----------------------------------------------------------------------
610634

611635
Before discussing another option, `--show-pulls`, we need to
612636
create a new example history.
@@ -662,7 +686,7 @@ Here, the merge commits `O` and `P` contribute extra noise, as they did
662686
not actually contribute a change to `file.txt`. They only merged a topic
663687
that was based on an older version of `file.txt`. This is a common
664688
issue in repositories using a workflow where many contributors work in
665-
parallel and merge their topic branches along a single trunk: manu
689+
parallel and merge their topic branches along a single trunk: many
666690
unrelated merges appear in the `--full-history` results.
667691

668692
When using the `--simplify-merges` option, the commits `O` and `P`

object.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct object_array {
5959

6060
/*
6161
* object flag allocation:
62-
* revision.h: 0---------10 15 23------26
62+
* revision.h: 0---------10 15 23------27
6363
* fetch-pack.c: 01 67
6464
* negotiator/default.c: 2--5
6565
* walker.c: 0-2

revision.c

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
11051105
struct commit_list **list, struct prio_queue *queue)
11061106
{
11071107
struct commit_list *parent = commit->parents;
1108-
unsigned left_flag;
1108+
unsigned pass_flags;
11091109

11101110
if (commit->object.flags & ADDED)
11111111
return 0;
@@ -1160,7 +1160,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
11601160
if (revs->no_walk)
11611161
return 0;
11621162

1163-
left_flag = (commit->object.flags & SYMMETRIC_LEFT);
1163+
pass_flags = (commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH));
11641164

11651165
for (parent = commit->parents; parent; parent = parent->next) {
11661166
struct commit *p = parent->item;
@@ -1181,7 +1181,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
11811181
if (!*slot)
11821182
*slot = *revision_sources_at(revs->sources, commit);
11831183
}
1184-
p->object.flags |= left_flag;
1184+
p->object.flags |= pass_flags;
11851185
if (!(p->object.flags & SEEN)) {
11861186
p->object.flags |= (SEEN | NOT_USER_GIVEN);
11871187
if (list)
@@ -1304,13 +1304,24 @@ static int still_interesting(struct commit_list *src, timestamp_t date, int slop
13041304
}
13051305

13061306
/*
1307-
* "rev-list --ancestry-path A..B" computes commits that are ancestors
1308-
* of B but not ancestors of A but further limits the result to those
1309-
* that are descendants of A. This takes the list of bottom commits and
1310-
* the result of "A..B" without --ancestry-path, and limits the latter
1311-
* further to the ones that can reach one of the commits in "bottom".
1307+
* "rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B"
1308+
* computes commits that are ancestors of B but not ancestors of A but
1309+
* further limits the result to those that have any of C in their
1310+
* ancestry path (i.e. are either ancestors of any of C, descendants
1311+
* of any of C, or are any of C). If --ancestry-path is specified with
1312+
* no commit, we use all bottom commits for C.
1313+
*
1314+
* Before this function is called, ancestors of C will have already
1315+
* been marked with ANCESTRY_PATH previously.
1316+
*
1317+
* This takes the list of bottom commits and the result of "A..B"
1318+
* without --ancestry-path, and limits the latter further to the ones
1319+
* that have any of C in their ancestry path. Since the ancestors of C
1320+
* have already been marked (a prerequisite of this function), we just
1321+
* need to mark the descendants, then exclude any commit that does not
1322+
* have any of these marks.
13121323
*/
1313-
static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
1324+
static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)
13141325
{
13151326
struct commit_list *p;
13161327
struct commit_list *rlist = NULL;
@@ -1323,7 +1334,7 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li
13231334
for (p = list; p; p = p->next)
13241335
commit_list_insert(p->item, &rlist);
13251336

1326-
for (p = bottom; p; p = p->next)
1337+
for (p = bottoms; p; p = p->next)
13271338
p->item->object.flags |= TMP_MARK;
13281339

13291340
/*
@@ -1356,38 +1367,39 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li
13561367
*/
13571368

13581369
/*
1359-
* The ones that are not marked with TMP_MARK are uninteresting
1370+
* The ones that are not marked with either TMP_MARK or
1371+
* ANCESTRY_PATH are uninteresting
13601372
*/
13611373
for (p = list; p; p = p->next) {
13621374
struct commit *c = p->item;
1363-
if (c->object.flags & TMP_MARK)
1375+
if (c->object.flags & (TMP_MARK | ANCESTRY_PATH))
13641376
continue;
13651377
c->object.flags |= UNINTERESTING;
13661378
}
13671379

1368-
/* We are done with the TMP_MARK */
1380+
/* We are done with TMP_MARK and ANCESTRY_PATH */
13691381
for (p = list; p; p = p->next)
1370-
p->item->object.flags &= ~TMP_MARK;
1371-
for (p = bottom; p; p = p->next)
1372-
p->item->object.flags &= ~TMP_MARK;
1382+
p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
1383+
for (p = bottoms; p; p = p->next)
1384+
p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
13731385
free_commit_list(rlist);
13741386
}
13751387

13761388
/*
1377-
* Before walking the history, keep the set of "negative" refs the
1378-
* caller has asked to exclude.
1389+
* Before walking the history, add the set of "negative" refs the
1390+
* caller has asked to exclude to the bottom list.
13791391
*
13801392
* This is used to compute "rev-list --ancestry-path A..B", as we need
13811393
* to filter the result of "A..B" further to the ones that can actually
13821394
* reach A.
13831395
*/
1384-
static struct commit_list *collect_bottom_commits(struct commit_list *list)
1396+
static void collect_bottom_commits(struct commit_list *list,
1397+
struct commit_list **bottom)
13851398
{
1386-
struct commit_list *elem, *bottom = NULL;
1399+
struct commit_list *elem;
13871400
for (elem = list; elem; elem = elem->next)
13881401
if (elem->item->object.flags & BOTTOM)
1389-
commit_list_insert(elem->item, &bottom);
1390-
return bottom;
1402+
commit_list_insert(elem->item, bottom);
13911403
}
13921404

13931405
/* Assumes either left_only or right_only is set */
@@ -1414,12 +1426,12 @@ static int limit_list(struct rev_info *revs)
14141426
struct commit_list *original_list = revs->commits;
14151427
struct commit_list *newlist = NULL;
14161428
struct commit_list **p = &newlist;
1417-
struct commit_list *bottom = NULL;
14181429
struct commit *interesting_cache = NULL;
14191430

1420-
if (revs->ancestry_path) {
1421-
bottom = collect_bottom_commits(original_list);
1422-
if (!bottom)
1431+
if (revs->ancestry_path_implicit_bottoms) {
1432+
collect_bottom_commits(original_list,
1433+
&revs->ancestry_path_bottoms);
1434+
if (!revs->ancestry_path_bottoms)
14231435
die("--ancestry-path given but there are no bottom commits");
14241436
}
14251437

@@ -1464,9 +1476,8 @@ static int limit_list(struct rev_info *revs)
14641476
if (revs->left_only || revs->right_only)
14651477
limit_left_right(newlist, revs);
14661478

1467-
if (bottom)
1468-
limit_to_ancestry(bottom, newlist);
1469-
free_commit_list(bottom);
1479+
if (revs->ancestry_path)
1480+
limit_to_ancestry(revs->ancestry_path_bottoms, newlist);
14701481

14711482
/*
14721483
* Check if any commits have become TREESAME by some of their parents
@@ -2213,7 +2224,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
22132224
const struct setup_revision_opt* opt)
22142225
{
22152226
const char *arg = argv[0];
2216-
const char *optarg;
2227+
const char *optarg = NULL;
22172228
int argcount;
22182229
const unsigned hexsz = the_hash_algo->hexsz;
22192230

@@ -2284,6 +2295,23 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
22842295
revs->ancestry_path = 1;
22852296
revs->simplify_history = 0;
22862297
revs->limited = 1;
2298+
revs->ancestry_path_implicit_bottoms = 1;
2299+
} else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
2300+
struct commit *c;
2301+
struct object_id oid;
2302+
const char *msg = _("could not get commit for ancestry-path argument %s");
2303+
2304+
revs->ancestry_path = 1;
2305+
revs->simplify_history = 0;
2306+
revs->limited = 1;
2307+
2308+
if (repo_get_oid_committish(revs->repo, optarg, &oid))
2309+
return error(msg, optarg);
2310+
get_reference(revs, optarg, &oid, ANCESTRY_PATH);
2311+
c = lookup_commit_reference(revs->repo, &oid);
2312+
if (!c)
2313+
return error(msg, optarg);
2314+
commit_list_insert(c, &revs->ancestry_path_bottoms);
22872315
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
22882316
init_reflog_walk(&revs->reflog_info);
22892317
} else if (!strcmp(arg, "--default")) {
@@ -2993,6 +3021,7 @@ static void release_revisions_topo_walk_info(struct topo_walk_info *info);
29933021
void release_revisions(struct rev_info *revs)
29943022
{
29953023
free_commit_list(revs->commits);
3024+
free_commit_list(revs->ancestry_path_bottoms);
29963025
object_array_clear(&revs->pending);
29973026
object_array_clear(&revs->boundary_commits);
29983027
release_revisions_cmdline(&revs->cmdline);

revision.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
*/
4949
#define NOT_USER_GIVEN (1u<<25)
5050
#define TRACK_LINEAR (1u<<26)
51+
#define ANCESTRY_PATH (1u<<27)
5152
#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)
5253

5354
#define DECORATE_SHORT_REFS 1
@@ -164,6 +165,13 @@ struct rev_info {
164165
cherry_mark:1,
165166
bisect:1,
166167
ancestry_path:1,
168+
169+
/* True if --ancestry-path was specified without an
170+
* argument. The bottom revisions are implicitly
171+
* the arguments in this case.
172+
*/
173+
ancestry_path_implicit_bottoms:1,
174+
167175
first_parent_only:1,
168176
exclude_first_parent_only:1,
169177
line_level_traverse:1,
@@ -306,6 +314,7 @@ struct rev_info {
306314
struct saved_parents *saved_parents_slab;
307315

308316
struct commit_list *previous_parents;
317+
struct commit_list *ancestry_path_bottoms;
309318
const char *break_bar;
310319

311320
struct revision_sources *sources;

0 commit comments

Comments
 (0)