Allow --ancestry-path to take an argument
cover Allow --ancestry-path to take an argument
This came out of a previous thread[1], where I wanted to be able to run something like
git log --oneline --ancestry-path=ab/submodule-cleanup main..seen
and see the commits in main..seen which contained ab/submodule-cleanup in their ancestry path. Let me start by defining the terminology "X is in a commit's ancestry path". By that, I just mean that either the commit is X, the commit is an ancestor of X, or the commit is a descendant of X. With that definition...
The command
git log --ancestry-path A..B
means find the commits in A..B which contain A in their ancestry path. I sometimes still want to use A..B to get the basic range, but would like to use a commit other than A for specifying which ancestry path is of interest. So, for example, I might want to use
git log --ancestry-path=C A..B
to mean find the commits in A..B which contain C in their ancestry path, or use
git log --ancestry-path=C --ancestry-path=D A..B
to mean find the commits in A..B which contain either C or D in their ancestry path.
This series implements this request, by allowing --ancestry-path to take an optional argument. With it, I can find the answer to my question in the thread at [1] within the git.git repository (replacing branch names with actual hashes since the branches have since moved on):
$ git log --oneline --ancestry-path=5b893f7d81 8168d5e9c2..ac0248bfba | wc -l 36
This returns the answer I want, whereas dropping the '=5b893f7d81' from the command line gives me 192 unwanted commits (228 total), and various other command line flags (--first-parent, --boundary, etc.) also fail to give me the set of commits I am looking for.
[1]
https://lore.kernel.org/git/CABPp-BF+8aqysioP_e27Q9kJ02rE2SuSqXu+XphzKWnk5a_Q+A@mail.gmail.com/
Elijah Newren (2):
rev-list-options.txt: fix simple typo
revision: allow --ancestry-path to take an argument
Documentation/rev-list-options.txt | 47 +++++++++++++---- object.h | 2 +- revision.c | 83 ++++++++++++++++++++---------- revision.h | 3 ++ t/t6019-rev-list-ancestry-path.sh | 47 ++++++++++++++++- 5 files changed, 140 insertions(+), 42 deletions(-)
base-commit: 6a475b71f8c4ce708d69fdc9317aefbde3769e25
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1303%2Fnewren%2Fancestry-path-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1303/newren/ancestry-path-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1303
Signature · 1 line
gitgitgadget
PATCH 1/2 rev-list-options.txt: fix simple typo
From: Elijah Newren <[email protected]>
--- Documentation/rev-list-options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/rev-list-options.txt +1 -1
@@ -659,7 +659,7 @@ Here, the merge commits `O` and `P` contribute extra noise, as they did not actually contribute a change to `file.txt`. They only merged a topic that was based on an older version of `file.txt`. This is a common issue in repositories using a workflow where many contributors work in -parallel and merge their topic branches along a single trunk: manu +parallel and merge their topic branches along a single trunk: many unrelated merges appear in the `--full-history` results. When using the `--simplify-merges` option, the commits `O` and `P`
Signature · 1 line
gitgitgadget
PATCH 2/2 revision: allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetFrom: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path next..seen
which shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor next
* have next as an ancestor
This commit allows another variant:
git log --ancestry-path=$TOPIC next..seen
which shows all commits which satisfy all of these criteria:
* are an ancestor of seen
* are not an ancestor of next
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
* are an ancestor of $TOPIC
* have $TOPIC as an ancestor
* are $TOPIC
This also allows multiple --ancestry-path arguments, which can be used to find commits with any of the given topics in their ancestry path.
--- Documentation/rev-list-options.txt | 45 ++++++++++++---- object.h | 2 +- revision.c | 83 ++++++++++++++++++++---------- revision.h | 3 ++ t/t6019-rev-list-ancestry-path.sh | 47 ++++++++++++++++- 5 files changed, 139 insertions(+), 41 deletions(-)
Documentation/rev-list-options.txt +35 -10
@@ -389,12 +389,15 @@ Default mode:: merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path:: +--ancestry-path[=<commit>]:: When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), only display commits that exist - directly on the ancestry chain between the 'commit1' and - 'commit2', i.e. commits that are both descendants of 'commit1', - and ancestors of 'commit2'. + or 'commit2 {caret}commit1'), only display commits in that + range where <commit> is part of the ancestry chain. By "part of + the ancestry chain", we mean including <commit> itself and + commits that are either ancestors or descendants of <commit>. + If no commit is specified, use 'commit1' (the excluded part of + the range) as <commit>. Can be passed multiple times to look for + commits in the ancestry range of multiple commits. A more detailed explanation follows.
@@ -568,11 +571,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available: ---ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the ``from'' and ``to'' commits in the given commit - range. I.e. only display commits that are ancestor of the ``to'' - commit and descendants of the ``from'' commit. +--ancestry-path[=<commit>]:: + Limit the displayed commits to those containing <commit> in their + ancestry path. I.e. only display <commit> and commits which have + <commit> as either a direct ancestor or descendant. + As an example use case, consider the following commit history: +
@@ -604,6 +606,29 @@ option does. Applied to the 'D..M' range, it results in: \ L--M ----------------------------------------------------------------------- ++ +We can also use `--ancestry-path=D` instead of `--ancestry-path` which +means the same thing when applied to the 'D..M' range but is just more +explicit. ++ +If we instead are interested in a given topic within this range, and all +commits affected by that topic, we may only want to view the subset of +`D..M` which contain that topic in their ancestry path. So, using +`--ancestry-path=H D..M` for example would result in: ++ +----------------------------------------------------------------------- + E + \ + G---H---I---J + \ + L--M +----------------------------------------------------------------------- ++ +Whereas `--ancestry-path=K D..M` would result in ++ +----------------------------------------------------------------------- + K---------------L--M +----------------------------------------------------------------------- Before discussing another option, `--show-pulls`, we need to create a new example history.
object.h +1 -1
@@ -59,7 +59,7 @@ struct object_array {/* * object flag allocation: -* revision.h: 0---------10 15 23------26 +* revision.h: 0---------10 15 23------27 * fetch-pack.c: 01 67 * negotiator/default.c: 2--5 * walker.c: 0-2
revision.c +55 -28
176 lines not on this page · raw message
revision.h +3 -0
@@ -48,6 +48,7 @@*/ #define NOT_USER_GIVEN (1u<<25)#define TRACK_LINEAR (1u<<26) +#define ANCESTRY_PATH (1u<<27)#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)#define DECORATE_SHORT_REFS 1
@@ -164,6 +165,7 @@ struct rev_info { cherry_mark:1, bisect:1, ancestry_path:1, + ancestry_path_need_bottoms:1, first_parent_only:1, exclude_first_parent_only:1, line_level_traverse:1,
@@ -306,6 +308,7 @@ struct rev_info {struct saved_parents *saved_parents_slab;struct commit_list *previous_parents; +struct commit_list *ancestry_path_bottoms;const char *break_bar;struct revision_sources *sources;
t/t6019-rev-list-ancestry-path.sh +45 -2
59 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH 2/2] revision: allow --ancestry-path to take an argument
Quoted 8 lines · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path next..seenwhich shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor next
* have next as an ancestor
Is it a very good example, though? Nothing builds on next, and next is not an ancestor of seen, so the command without --ancestry-path does give us individual commits that are not in 'next' yet, plus all the merge commits in master..seen but with --ancestry-path the answer is most likely an empty set.
If you replace 'next' with 'master', it does start to make sense, but that is a bit too straight first-parent merge chain that may not be all that interesting.
Quoted 11 lines
This commit allows another variant:
git log --ancestry-path=$TOPIC next..seenwhich shows all commits which satisfy all of these criteria:
* are an ancestor of seen
* are not an ancestor of next
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
* are an ancestor of $TOPIC
* have $TOPIC as an ancestor
* are $TOPIC
So, I have en/ancestry-path-in-a-range topic merged somewhere between 'master' and 'seen'. Here is what I see:
$ seen/git log --oneline --ancestry-path=en/ancestry-path-in-a-range master..seen
21aef6c754 Merge branch 'ab/submodule-helper-leakfix' into seen
2a57fcc25e Merge branch 'ab/submodule-helper-prep' into seen
72ff5f5d3a ###
edb5cf4c31 Merge branch 'cw/remote-object-info' into seen
cc8f65a665 Merge branch 'ag/merge-strategies-in-c' into seen
c1bacacabf Merge branch 'es/mark-gc-cruft-as-experimental' into seen
fdf2d207d2 Merge branch 'js/bisect-in-c' into seen
2a1bbfc016 Merge branch 'po/glossary-around-traversal' into seen
7ecf004b9e Merge branch 'vd/scalar-enables-fsmonitor' into jch
9dba189986 Merge branch 'en/ancestry-path-in-a-range' into jch
4461e34d7d revision: allow --ancestry-path to take an argument
0605b4aad9 rev-list-options.txt: fix simple typowhich is very much expected. Two commits are what we want to highlight, and its merge into the first-parent-chain that leads to 'seen', and all its descendants on 'seen' are shown.
Due to the way "ancestry-path" is defined, replacing the value to "--ancestry-path" from 4461e34d7d to 0605b4aad9 would not change the output, which is also expected. If this were a three-commit topic, giving the middle commit would find both the first one (i.e. the ancestor) and the third one (i.e. the descendant) in the topic, while excluding the much-less-interesting base commit and its ancestors the topic builds on.
I am not exactly sure when this feature is useful, though. It is handy to be able to enumerate descendants of a given commit, so perhaps the user knows about 0605b4aad9 and is trying to find other commits on the same topic, or something? But then the merges nearer the tip of 'seen' than 9dba189986 are not very useful for that purpose. It somehow feels like a solution in search of a problem.
Quoted 14 lines
revision.c +1
-1@@ -1105,7 +1105,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit, struct commit_list **list, struct prio_queue *queue) { struct commit_list *parent = commit->parents; -unsigned left_flag; +unsigned left_flag, ancestry_flag;if (commit->object.flags &ADDED )return 0 ;
revision.c static int process_parents(struct rev_info *revs, struct commit *commit, · line 1164
Quoted 4 lines
revision.c +1
-0@@ -1161,6 +1161,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,return 0 ; left_flag = (commit->object.flags &SYMMETRIC_LEFT ); + ancestry_flag = (commit->object.flags &ANCESTRY_PATH );
Wouldn't we want
if (revs->ancestry_path) ancestry_flag = (commit->object.flags & ANCESTRY_PATH);
instead, so that the propagation of contaminated flag bits ...
revision.c static int process_parents(struct rev_info *revs, struct commit *commit, · line 1187
Quoted 9 lines
revision.c +2
-0for (parent = commit->parents; parent; parent = parent->next) {struct commit *p = parent->item;@@ -1181,6 +1182,8 @@ static int process_parents(struct rev_info *revs, struct commit *commit,if (!*slot) *slot = *revision_sources_at (revs->sources, commit); } +if (revs->ancestry_path) + p->object.flags |= ancestry_flag; p->object.flags |= left_flag;
... can become a simple
p->object.flags |= ancestry_flag;
here? Or even just use a single variable to compute the set of flags to pass down i.e.
pass_flags = commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH);
before the loop, and then pass these two bits down at once, i.e.
- p->object.flags |= left_flag; + p->object.flags |= pass_flags;
taking advantage of the fact that ANCESTRY_PATH and SYMMETRIC_LEFT bits can be set to any object only when these features are in use?
Or did I misread the patch and sometimes ANCESTRY_PATH bit is set on objects even when revs->ancestry_path is not in use?
Quoted 30 lines
revision.c +5
-4+static void limit_to_ancestry (struct commit_list *bottoms,struct commit_list *list) {struct commit_list *p;struct commit_list *rlist =NULL ;@@ -1323,7 +1333,7 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *lifor (p = list; p; p = p->next)commit_list_insert (p->item, &rlist); -for (p = bottom; p; p = p->next) +for (p = bottoms; p; p = p->next) p->item->object.flags |=TMP_MARK ;/* @@ -1356,38 +1366,39 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li*/ /* -* The ones that are not marked with TMP_MARK are uninteresting +* The ones that are not marked with either TMP_MARK or +* ANCESTRY_PATH are uninteresting */ for (p = list; p; p = p->next) {struct commit *c = p->item; -if (c->object.flags &TMP_MARK ) +if (c->object.flags & (TMP_MARK |ANCESTRY_PATH ))continue ; c->object.flags |=UNINTERESTING ; } -/* We are done with the TMP_MARK */
revision.c static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li · line 1385
Quoted 10 lines
revision.c +4
-3+/* We are done with TMP_MARK and ANCESTRY_PATH */ for (p = list; p; p = p->next) - p->item->object.flags &= ~TMP_MARK ; -for (p = bottom; p; p = p->next) - p->item->object.flags &= ~TMP_MARK ; + p->item->object.flags &= ~(TMP_MARK |ANCESTRY_PATH ); +for (p = bottoms; p; p = p->next) + p->item->object.flags &= ~(TMP_MARK |ANCESTRY_PATH );free_commit_list (rlist); }
We have called process_parents() to paint ancestor commits that can be reached from the commit(s) of interest. This helper function is called after that is done, and propagates the ancestry_path bit in the reverse direction, i.e. from parent to child.
Once we are done with this processing, we no longer need ANCESTRY_PATH bit because the surviving ones without UNINTERESTING bit set are the commits on the ancestry_path. OK.
Re: [PATCH 2/2] revision: allow --ancestry-path to take an argument
Quoted 18 lines · Junio C Hamano
Quoted 8 lines · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path next..seenwhich shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor next
* have next as an ancestorIs it a very good example, though? Nothing builds on next, and next is not an ancestor of seen, so the command without --ancestry-path does give us individual commits that are not in 'next' yet, plus all the merge commits in master..seen but with --ancestry-path the answer is most likely an empty set.
Quoted 3 lines
If you replace 'next' with 'master', it does start to make sense, but that is a bit too straight first-parent merge chain that may not be all that interesting.
Yeah, I should have written master..seen.
Quoted 33 lines
Quoted 11 lines
This commit allows another variant:
git log --ancestry-path=$TOPIC next..seenwhich shows all commits which satisfy all of these criteria:
* are an ancestor of seen
* are not an ancestor of next
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
* are an ancestor of $TOPIC
* have $TOPIC as an ancestor
* are $TOPICSo, I have en/ancestry-path-in-a-range topic merged somewhere between 'master' and 'seen'. Here is what I see:
$ seen/git log --oneline --ancestry-path=en/ancestry-path-in-a-range master..seen 21aef6c754 Merge branch 'ab/submodule-helper-leakfix' into seen 2a57fcc25e Merge branch 'ab/submodule-helper-prep' into seen 72ff5f5d3a ### edb5cf4c31 Merge branch 'cw/remote-object-info' into seen cc8f65a665 Merge branch 'ag/merge-strategies-in-c' into seen c1bacacabf Merge branch 'es/mark-gc-cruft-as-experimental' into seen fdf2d207d2 Merge branch 'js/bisect-in-c' into seen 2a1bbfc016 Merge branch 'po/glossary-around-traversal' into seen 7ecf004b9e Merge branch 'vd/scalar-enables-fsmonitor' into jch 9dba189986 Merge branch 'en/ancestry-path-in-a-range' into jch 4461e34d7d revision: allow --ancestry-path to take an argument 0605b4aad9 rev-list-options.txt: fix simple typowhich is very much expected. Two commits are what we want to highlight, and its merge into the first-parent-chain that leads to 'seen', and all its descendants on 'seen' are shown.
Quoted 7 lines
Due to the way "ancestry-path" is defined, replacing the value to "--ancestry-path" from
4461e34d7dto0605b4aad9would not change the output, which is also expected. If this were a three-commit topic, giving the middle commit would find both the first one (i.e. the ancestor) and the third one (i.e. the descendant) in the topic, while excluding the much-less-interesting base commit and its ancestors the topic builds on.
Yes.
Quoted 6 lines
I am not exactly sure when this feature is useful, though. It is handy to be able to enumerate descendants of a given commit, so perhaps the user knows about
0605b4aad9and is trying to find other commits on the same topic, or something? But then the merges nearer the tip of 'seen' than9dba189986are not very useful for that purpose. It somehow feels like a solution in search of a problem.
Here's my usecase:
git replay --i --keep-base --contained --ancestry-path=$TOPIC master..seen
Explanation of usecase: I want to be able to replay commits, much like the current interactive rebase feature. Unlike rebase, the selection of commits is done via a standard <revision range>, for flexibility. Now, replaying of commits in $TOPIC implies I probably want all the topics that depend on it to be updated, and all the merges that depend on any of those to be updated. But, to achieve that, I don't want to have to manually run N rebases and manually remerge things and whatnot -- I want it all done in one command. (And yes, replaying of merges needs to handle squashed-in semantic fixes and replay those.) So, I want this command to replay all commits in the ancestry path of $TOPIC in the range master..seen, keeping the current base (--keep-base), and I want it to also update any --contained branches (meaning any branches pointing to commits in the range being replayed).
Alternatives: For this usecase,
* I cannot just use $TOPIC_TO_EDIT..seen, because that excludes the commits I want to tweak.
* I could technically use --ancestry-path master..seen, but it's almost uselessly suboptimal as it would put hundreds of irrelevant commits in the TODO list (making it hard for the user to find what they want to edit) and then wastes time replaying all those commits unnecessarily as well.
* As far as I can tell, there are no good alternatives here; and my question about it on the list didn't spur any satisfying answers[1].
If you'd rather I waited to submit the patch as part of a much larger series implementing git-replay, I can do that. I just thought it was useful to send upstream independently. But then I ran into the problem that I thought it was weird to describe a usecase depending on something that isn't yet very usable, and tried to explain it without that.
[1] https://lore.kernel.org/git/CABPp-BEqWX3Nr2HDxwS9d-_QjcKb_jS=fSjsP_Pbutw7-P5gbg@mail.gmail.com/
Quoted 50 lines
Quoted 14 lines
revision.c +1
-1@@ -1105,7 +1105,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit, struct commit_list **list, struct prio_queue *queue) { struct commit_list *parent = commit->parents; -unsigned left_flag; +unsigned left_flag, ancestry_flag;if (commit->object.flags &ADDED )return 0 ;revision.c static int process_parents(struct rev_info *revs, struct commit *commit, · line 1164
Quoted 4 lines
revision.c +1
-0@@ -1161,6 +1161,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,return 0 ; left_flag = (commit->object.flags &SYMMETRIC_LEFT ); + ancestry_flag = (commit->object.flags &ANCESTRY_PATH );Wouldn't we want
if (revs->ancestry_path) ancestry_flag = (commit->object.flags & ANCESTRY_PATH);instead, so that the propagation of contaminated flag bits ...
revision.c static int process_parents(struct rev_info *revs, struct commit *commit, · line 1187
Quoted 9 lines
revision.c +2
-0for (parent = commit->parents; parent; parent = parent->next) {struct commit *p = parent->item;@@ -1181,6 +1182,8 @@ static int process_parents(struct rev_info *revs, struct commit *commit,if (!*slot) *slot = *revision_sources_at (revs->sources, commit); } +if (revs->ancestry_path) + p->object.flags |= ancestry_flag; p->object.flags |= left_flag;... can become a simple
p->object.flags |= ancestry_flag;here? Or even just use a single variable to compute the set of flags to pass down i.e.
pass_flags = commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH);before the loop, and then pass these two bits down at once, i.e.
(quoted diff) +1
-1- p->object.flags |= left_flag; + p->object.flags |= pass_flags;
Quoted 2 lines
taking advantage of the fact that
ANCESTRY_PATHandSYMMETRIC_LEFTbits can be set to any object only when these features are in use?
Ooh, that does seem better. I'll make that change.
Quoted 52 lines
Or did I misread the patch and sometimes
ANCESTRY_PATHbit is set on objects even whenrevs->ancestry_pathis not in use?Quoted 30 lines
revision.c +5
-4+static void limit_to_ancestry (struct commit_list *bottoms,struct commit_list *list) {struct commit_list *p;struct commit_list *rlist =NULL ;@@ -1323,7 +1333,7 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *lifor (p = list; p; p = p->next)commit_list_insert (p->item, &rlist); -for (p = bottom; p; p = p->next) +for (p = bottoms; p; p = p->next) p->item->object.flags |=TMP_MARK ;/* @@ -1356,38 +1366,39 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li*/ /* -* The ones that are not marked with TMP_MARK are uninteresting +* The ones that are not marked with either TMP_MARK or +* ANCESTRY_PATH are uninteresting */ for (p = list; p; p = p->next) {struct commit *c = p->item; -if (c->object.flags &TMP_MARK ) +if (c->object.flags & (TMP_MARK |ANCESTRY_PATH ))continue ; c->object.flags |=UNINTERESTING ; } -/* We are done with the TMP_MARK */ revision.c static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li · line 1385
Quoted 10 lines
revision.c +4
-3+/* We are done with TMP_MARK and ANCESTRY_PATH */ for (p = list; p; p = p->next) - p->item->object.flags &= ~TMP_MARK ; -for (p = bottom; p; p = p->next) - p->item->object.flags &= ~TMP_MARK ; + p->item->object.flags &= ~(TMP_MARK |ANCESTRY_PATH ); +for (p = bottoms; p; p = p->next) + p->item->object.flags &= ~(TMP_MARK |ANCESTRY_PATH );free_commit_list (rlist); }We have called
process_parents()to paint ancestor commits that can be reached from the commit(s) of interest. This helper function is called after that is done, and propagates theancestry_pathbit in the reverse direction, i.e. from parent to child.Once we are done with this processing, we no longer need
ANCESTRY_PATHbit because the surviving ones without UNINTERESTING bit set are the commits on theancestry_path. OK.
v2 cover Allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetChanges since v1:
* Tweaked the commit message, and incorporated Junio's suggestion to update left_flag and ancestry_flag together.
Series description:
This came out of a previous thread[1], where I wanted to be able to run something like
git log --oneline --ancestry-path=ab/submodule-cleanup main..seen
and see the commits in main..seen which contained ab/submodule-cleanup in their ancestry path. Let me start by defining the terminology "X is in a commit's ancestry path". By that, I just mean that either the commit is X, the commit is an ancestor of X, or the commit is a descendant of X. With that definition...
The command
git log --ancestry-path A..B
means find the commits in A..B which contain A in their ancestry path. I sometimes still want to use A..B to get the basic range, but would like to use a commit other than A for specifying which ancestry path is of interest. So, for example, I might want to use
git log --ancestry-path=C A..B
to mean find the commits in A..B which contain C in their ancestry path, or use
git log --ancestry-path=C --ancestry-path=D A..B
to mean find the commits in A..B which contain either C or D in their ancestry path.
This series implements this request, by allowing --ancestry-path to take an optional argument. With it, I can find the answer to my question in the thread at [1] within the git.git repository (replacing branch names with actual hashes since the branches have since moved on):
$ git log --oneline --ancestry-path=5b893f7d81 8168d5e9c2..ac0248bfba | wc -l 36
This returns the answer I want, whereas dropping the '=5b893f7d81' from the command line gives me 192 unwanted commits (228 total), and various other command line flags (--first-parent, --boundary, etc.) also fail to give me the set of commits I am looking for.
[1]
https://lore.kernel.org/git/CABPp-BF+8aqysioP_e27Q9kJ02rE2SuSqXu+XphzKWnk5a_Q+A@mail.gmail.com/
Elijah Newren (2):
rev-list-options.txt: fix simple typo
revision: allow --ancestry-path to take an argument
Documentation/rev-list-options.txt | 47 +++++++++++++---- object.h | 2 +- revision.c | 84 +++++++++++++++++++----------- revision.h | 3 ++ t/t6019-rev-list-ancestry-path.sh | 47 ++++++++++++++++- 5 files changed, 139 insertions(+), 44 deletions(-)
base-commit: 6a475b71f8c4ce708d69fdc9317aefbde3769e25
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1303%2Fnewren%2Fancestry-path-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1303/newren/ancestry-path-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1303
Range-diff vs v1:
1: 68ab719d99c = 1: 68ab719d99c rev-list-options.txt: fix simple typo 2: 99287b67fd1 ! 2: f580ec6d060 revision: allow --ancestry-path to take an argument
@@ Commit message
revision: allow --ancestry-path to take an argument
We have long allowed users to run e.g.
- git log --ancestry-path next..seen
+ git log --ancestry-path master..seen
which shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
- * are not an ancestor next
- * have next as an ancestor
+ * are not an ancestor master
+ * have master as an ancestor
This commit allows another variant:
- git log --ancestry-path=$TOPIC next..seen
+ git log --ancestry-path=$TOPIC master..seen
which shows all commits which satisfy all of these criteria:
* are an ancestor of seen
- * are not an ancestor of next
+ * are not an ancestor of master
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
@@ revision.c: static int process_parents(struct rev_info *revs, struct commit *com
{
struct commit_list *parent = commit->parents;
- unsigned left_flag;
-+ unsigned left_flag, ancestry_flag;
++ unsigned pass_flags;
if (commit->object.flags & ADDED)
return 0;
@@ revision.c: static int process_parents(struct rev_info *revs, struct commit *commit,
+ if (revs->no_walk)
return 0;
- left_flag = (commit->object.flags & SYMMETRIC_LEFT);
-+ ancestry_flag = (commit->object.flags & ANCESTRY_PATH);
+- left_flag = (commit->object.flags & SYMMETRIC_LEFT);
++ pass_flags = (commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH));
for (parent = commit->parents; parent; parent = parent->next) {
struct commit *p = parent->item;
@@ revision.c: static int process_parents(struct rev_info *revs, struct commit *com
if (!*slot)
*slot = *revision_sources_at(revs->sources, commit);
}
-+ if (revs->ancestry_path)
-+ p->object.flags |= ancestry_flag;
- p->object.flags |= left_flag;
+- p->object.flags |= left_flag;
++ p->object.flags |= pass_flags;
if (!(p->object.flags & SEEN)) {
p->object.flags |= (SEEN | NOT_USER_GIVEN);
+ if (list)
@@ revision.c: static int still_interesting(struct commit_list *src, timestamp_t date, int slop
}
Signature · 1 line
gitgitgadget
PATCH v2 1/2 rev-list-options.txt: fix simple typo
From: Elijah Newren <[email protected]>
--- Documentation/rev-list-options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/rev-list-options.txt +1 -1
@@ -659,7 +659,7 @@ Here, the merge commits `O` and `P` contribute extra noise, as they did not actually contribute a change to `file.txt`. They only merged a topic that was based on an older version of `file.txt`. This is a common issue in repositories using a workflow where many contributors work in -parallel and merge their topic branches along a single trunk: manu +parallel and merge their topic branches along a single trunk: many unrelated merges appear in the `--full-history` results. When using the `--simplify-merges` option, the commits `O` and `P`
Signature · 1 line
gitgitgadget
PATCH v2 2/2 revision: allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetFrom: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path master..seen
which shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor master
* have master as an ancestor
This commit allows another variant:
git log --ancestry-path=$TOPIC master..seen
which shows all commits which satisfy all of these criteria:
* are an ancestor of seen
* are not an ancestor of master
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
* are an ancestor of $TOPIC
* have $TOPIC as an ancestor
* are $TOPIC
This also allows multiple --ancestry-path arguments, which can be used to find commits with any of the given topics in their ancestry path.
--- Documentation/rev-list-options.txt | 45 ++++++++++++---- object.h | 2 +- revision.c | 84 +++++++++++++++++++----------- revision.h | 3 ++ t/t6019-rev-list-ancestry-path.sh | 47 ++++++++++++++++- 5 files changed, 138 insertions(+), 43 deletions(-)
Documentation/rev-list-options.txt +35 -10
@@ -389,12 +389,15 @@ Default mode:: merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path:: +--ancestry-path[=<commit>]:: When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), only display commits that exist - directly on the ancestry chain between the 'commit1' and - 'commit2', i.e. commits that are both descendants of 'commit1', - and ancestors of 'commit2'. + or 'commit2 {caret}commit1'), only display commits in that + range where <commit> is part of the ancestry chain. By "part of + the ancestry chain", we mean including <commit> itself and + commits that are either ancestors or descendants of <commit>. + If no commit is specified, use 'commit1' (the excluded part of + the range) as <commit>. Can be passed multiple times to look for + commits in the ancestry range of multiple commits. A more detailed explanation follows.
@@ -568,11 +571,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available: ---ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the ``from'' and ``to'' commits in the given commit - range. I.e. only display commits that are ancestor of the ``to'' - commit and descendants of the ``from'' commit. +--ancestry-path[=<commit>]:: + Limit the displayed commits to those containing <commit> in their + ancestry path. I.e. only display <commit> and commits which have + <commit> as either a direct ancestor or descendant. + As an example use case, consider the following commit history: +
@@ -604,6 +606,29 @@ option does. Applied to the 'D..M' range, it results in: \ L--M ----------------------------------------------------------------------- ++ +We can also use `--ancestry-path=D` instead of `--ancestry-path` which +means the same thing when applied to the 'D..M' range but is just more +explicit. ++ +If we instead are interested in a given topic within this range, and all +commits affected by that topic, we may only want to view the subset of +`D..M` which contain that topic in their ancestry path. So, using +`--ancestry-path=H D..M` for example would result in: ++ +----------------------------------------------------------------------- + E + \ + G---H---I---J + \ + L--M +----------------------------------------------------------------------- ++ +Whereas `--ancestry-path=K D..M` would result in ++ +----------------------------------------------------------------------- + K---------------L--M +----------------------------------------------------------------------- Before discussing another option, `--show-pulls`, we need to create a new example history.
object.h +1 -1
@@ -59,7 +59,7 @@ struct object_array {/* * object flag allocation: -* revision.h: 0---------10 15 23------26 +* revision.h: 0---------10 15 23------27 * fetch-pack.c: 01 67 * negotiator/default.c: 2--5 * walker.c: 0-2
revision.c +54 -30
177 lines not on this page · raw message
revision.h +3 -0
@@ -48,6 +48,7 @@*/ #define NOT_USER_GIVEN (1u<<25)#define TRACK_LINEAR (1u<<26) +#define ANCESTRY_PATH (1u<<27)#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)#define DECORATE_SHORT_REFS 1
@@ -164,6 +165,7 @@ struct rev_info { cherry_mark:1, bisect:1, ancestry_path:1, + ancestry_path_need_bottoms:1, first_parent_only:1, exclude_first_parent_only:1, line_level_traverse:1,
@@ -306,6 +308,7 @@ struct rev_info {struct saved_parents *saved_parents_slab;struct commit_list *previous_parents; +struct commit_list *ancestry_path_bottoms;const char *break_bar;struct revision_sources *sources;
t/t6019-rev-list-ancestry-path.sh +45 -2
59 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 1 line · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
Code looks good!
Quoted 7 lines
t/t6019-rev-list-ancestry-path.sh +0
-0@@ -8,8 +8,13 @@ test_description='--ancestry-path' # / \ # A-------K---------------L--M
t/t6019-rev-list-ancestry-path.sh test_description='--ancestry-path' · line 17
Quoted 10 lines
t/t6019-rev-list-ancestry-path.sh +7
-2# -# D..M == E F G H I J K L M -# --ancestry-path D..M == E F H I J L M +# D..M == E F G H I J K L M +# --ancestry-path D..M == E F H I J L M +# --ancestry-path=F D..M == E F J L M +# --ancestry-path=G D..M == G H I J L M +# --ancestry-path=H D..M == E G H I J L M +# --ancestry-path=K D..M == K L M +# --ancestry-path=K --ancestry-path=F D..M == E F J K L M
These are good examples, because they help clarify what I had initially been confused about: you include things in _both_ directions. In particular, "--ancestry-path=K --ancestry-path=f D..M" you are kind of taking the union of the following queries:
--ancestry-path D..K
--ancestry-path K..M
--ancestry-path D..F
--ancestry-path F..M
I did check just in case, but specifying multiple ranges such as
--ancestry-path D..K K..M
does not do what is expected.
t/t6019-rev-list-ancestry-path.sh test_description='--ancestry-path'
Quoted 7 lines
t/t6019-rev-list-ancestry-path.sh +7
-0+test_expect_success 'rev-list --ancestry-path=F D..M' ' + test_write_lines E F J L M >expect && + git rev-list --ancestry-path=F --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +'
These tests follow the patterns from other tests in this file, but it also has bad patterns. Specifically, the 'git rev-list' command is fed directly into a pipe. I include a patch below that applies directly on this one to rewrite these tests. If you want, you could rebase to have that test refactor happen before you add your new --ancestry-path=<X> option tests.
t/t6019-rev-list-ancestry-path.sh
Quoted 8 lines
t/t6019-rev-list-ancestry-path.sh +8
-0+test_expect_success 'rev-list --ancestry-path=G D..M' ' + test_write_lines G H I J L M >expect && + git rev-list --ancestry-path=G --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' +test_expect_success 'rev-list --ancestry-path=H D..M' '
nit: needs extra whitespace between tests. The above test pair needs one, too. This becomes moot with the patch I provide.
Thanks,
-Stolee
--- >8 ---
From 9ac4e81dba0d7801513a09f5fe307d01357123dd Mon Sep 17 00:00:00 2001
From: Derrick Stolee <[email protected]>
Date: Thu, 18 Aug 2022 11:25:04 -0400
Subject: [PATCH] t6019: modernize tests with helper
The tests in t6019 are repetive, so create a helper that greatly simplifies the test script.
In addition, update the common pattern that places 'git rev-list' on the left side of a pipe, which can hide some exit codes. Send the output to a 'raw' file that is then consumed by other tools so the Git exit code is verified as zero.
--- t/t6019-rev-list-ancestry-path.sh | 131 +++++++----------------------- 1 file changed, 31 insertions(+), 100 deletions(-)
t/t6019-rev-list-ancestry-path.sh +31 -100
@@ -55,111 +55,42 @@ test_expect_success setup ' test_commit M ' -test_expect_success 'rev-list D..M' ' - test_write_lines E F G H I J K L M >expect && - git rev-list --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M' ' - test_write_lines E F H I J L M >expect && - git rev-list --ancestry-path --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=F D..M' ' - test_write_lines E F J L M >expect && - git rev-list --ancestry-path=F --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' -test_expect_success 'rev-list --ancestry-path=G D..M' ' - test_write_lines G H I J L M >expect && - git rev-list --ancestry-path=G --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' -test_expect_success 'rev-list --ancestry-path=H D..M' ' - test_write_lines E G H I J L M >expect && - git rev-list --ancestry-path=H --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=K D..M' ' - test_write_lines K L M >expect && - git rev-list --ancestry-path=K --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=F --ancestry-path=K D..M' ' - test_write_lines E F J K L M >expect && - git rev-list --ancestry-path=F --ancestry-path=K --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list D..M -- M.t' ' - echo M >expect && - git rev-list --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2 + test_expect_success "rev-list $args" " + test_write_lines $expected >expect && + git rev-list --format=%s $args >raw && + + if test -n \"$expected\" + then + sed -e \"/^commit /d\" raw | sort >actual && + test_cmp expect actual || return 1 + else + test_must_be_empty raw + fi + " +} -test_expect_success 'rev-list F...I' ' - test_write_lines F G H I >expect && - git rev-list --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "D..M" "E F G H I J K L M" -test_expect_success 'rev-list --ancestry-path F...I' ' - test_write_lines F H I >expect && - git rev-list --ancestry-path --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "--ancestry-path D..M" "E F H I J L M" +test_ancestry "--ancestry-path D..M" "E F H I J L M" +test_ancestry "--ancestry-path=F D..M" "E F J L M" +test_ancestry "--ancestry-path=G D..M" "G H I J L M" +test_ancestry "--ancestry-path=H D..M" "E G H I J L M" +test_ancestry "--ancestry-path=K D..M" "K L M" +test_ancestry "--ancestry-path=K --ancestry-path=F D..M" "E F J K L M" -# G.t is dropped in an "-s ours" merge -test_expect_success 'rev-list G..M -- G.t' ' - git rev-list --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_must_be_empty actual -' +test_ancestry "D..M -- M.t" "M" +test_ancestry "--ancestry-path D..M -- M.t" "M" -test_expect_success 'rev-list --ancestry-path G..M -- G.t' ' - echo L >expect && - git rev-list --ancestry-path --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry "F...I" "F G H I" +test_ancestry "--ancestry-path F...I" "F H I" -test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' ' - test_write_lines G L >expect && - git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "G..M -- G.t" "" +test_ancestry "--ancestry-path G..M -- G.t" "L" +test_ancestry "--ancestry-path --simplify-merges G^..M -- G.t" "G L" # b---bc # / \ /
Signature · 1 line
2.37.1.vfs.0.0.rebase
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 15 lines · Derrick Stolee
Review
Quoted 7 lines · Elijah Newren via GitGitGadget
(quoted diff) +7
-0+test_expect_success 'rev-list --ancestry-path=F D..M' ' + test_write_lines E F J L M >expect && + git rev-list --ancestry-path=F --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +'These tests follow the patterns from other tests in this file, but it also has bad patterns. Specifically, the 'git rev-list' command is fed directly into a pipe. I include a patch below that applies directly on this one to rewrite these tests. If you want, you could rebase to have that test refactor happen before you add your new --ancestry-path=<X> option tests.
Thanks, I was going to comment on the same, but your solution is much better (I was just going to suggest using intermediate files).
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 60
Quoted 10 lines
[...]
t/t6019-rev-list-ancestry-path.sh +3
-6-test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2
Maybe add &&-chaining here (we do it in some cases, but I'm not sure when such assignments would ever fail).
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 65
Quoted 5 lines
t/t6019-rev-list-ancestry-path.sh +5
-0+ test_expect_success "rev-list $args" " + test_write_lines $expected >expect && + git rev-list --format=%s $args >raw && + + if test -n \"$expected\"
Aren't you making things harder for yourself here than required by using ""-quoting for the body of the test.
We eval it into existence, so you can use ''-quotes, and then you don't need to escape e.g. the "" quotes here for expected, no?
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 67
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ then + sed -e \"/^commit /d\" raw | sort >actual &&
nit for debuggability (and not correctness), maybe using intermediate files here would be nicer? And then perhaps call them "actual" and "actual.sorted".
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 68
Quoted 1 line
t/t6019-rev-list-ancestry-path.sh +1
-0+ test_cmp expect actual || return 1
No need for a "return 1" here when we're not in a loop. It's redundant, and makes the -x output on failure confusing ("why didn't I fail on the test_cmp, but on this stray return?...").
...
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 70
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ else + test_must_be_empty raw
...which would also allow you to extract much of this if/else at the cost of not using test_must_be_empty, i.e. just make the "expected" empty unless "$expected" is provided. Just a thought/nit, we could also leave this as-is :)
Also does the "compare rev" part of this want test_cmp_rev instead?
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 22 lines · Ævar Arnfjörð Bjarmason
Quoted 15 lines · Derrick Stolee
Review
Quoted 7 lines · Elijah Newren via GitGitGadget
(quoted diff) +7
-0+test_expect_success 'rev-list --ancestry-path=F D..M' ' + test_write_lines E F J L M >expect && + git rev-list --ancestry-path=F --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +'These tests follow the patterns from other tests in this file, but it also has bad patterns. Specifically, the 'git rev-list' command is fed directly into a pipe. I include a patch below that applies directly on this one to rewrite these tests. If you want, you could rebase to have that test refactor happen before you add your new --ancestry-path=<X> option tests.
Thanks, I was going to comment on the same, but your solution is much better (I was just going to suggest using intermediate files).
Quoted 13 lines
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 60
Quoted 10 lines
[...]
t/t6019-rev-list-ancestry-path.sh +3
-6-test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2Maybe add &&-chaining here (we do it in some cases, but I'm not sure when such assignments would ever fail).
These are outside of a test_expect_success, so they are not important.
Quoted 11 lines
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 65
Quoted 5 lines
t/t6019-rev-list-ancestry-path.sh +5
-0+ test_expect_success "rev-list $args" " + test_write_lines $expected >expect && + git rev-list --format=%s $args >raw && + + if test -n \"$expected\"Aren't you making things harder for yourself here than required by using ""-quoting for the body of the test.
We eval it into existence, so you can use ''-quotes, and then you don't need to escape e.g. the "" quotes here for expected, no?
Are "args" and "expected" in-scope if I use single quotes? I don't think they are unless we export them. I could be wrong.
Quoted 6 lines
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 67
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ then + sed -e \"/^commit /d\" raw | sort >actual &&nit for debuggability (and not correctness), maybe using intermediate files here would be nicer? And then perhaps call them "actual" and "actual.sorted".
I don't think that level of debuggability is important. We have the raw file if we want to see what the Git output was.
Quoted 6 lines
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 68
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +1
-0+ test_cmp expect actual || return 1No need for a "return 1" here when we're not in a loop. It's redundant, and makes the -x output on failure confusing ("why didn't I fail on the
test_cmp, but on this stray return?...").
Sure. I did this more out of habit, but it makes sense that we don't need it for an if block.
Quoted 9 lines
...
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 70
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ else + test_must_be_empty raw...which would also allow you to extract much of this if/else at the cost of not using
test_must_be_empty, i.e. just make the "expected" empty unless "$expected" is provided. Just a thought/nit, we could also leave this as-is :)
*shrug* either way is fine by me.
Quoted 1 line
Also does the "compare rev" part of this want
test_cmp_revinstead?
I don't know what you mean here. We are not comparing revisions anywhere, but we are using the commit messages to create an easy comparison for the output.
Thanks,
-Stolee
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
In reply to Ævar Arnfjörð BjarmasonQuoted 7 lines · Ævar Arnfjörð Bjarmason
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 60
Quoted 3 lines · Derrick Stolee
t/t6019-rev-list-ancestry-path.sh +3
-0+test_ancestry () { + args=$1 + expected=$2Maybe add &&-chaining here (we do it in some cases, but I'm not sure when such assignments would ever fail).
Assignment shouldn't fail, but keeping the &&-chain intact here protects us against the unlikely event of someone inserting &&-chained code above these assignments and not realizing that the &&-chain is not intact at the assignment lines.
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
In reply to Ævar Arnfjörð BjarmasonQuoted 48 lines · Ævar Arnfjörð Bjarmason
Quoted 9 lines · Derrick Stolee
Review
Quoted 7 lines · Elijah Newren via GitGitGadget
(quoted diff) +7
-0+test_expect_success 'rev-list --ancestry-path=F D..M' ' + test_write_lines E F J L M >expect && + git rev-list --ancestry-path=F --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +'Quoted 6 lines
These tests follow the patterns from other tests in this file, but it also has bad patterns. Specifically, the 'git rev-list' command is fed directly into a pipe. I include a patch below that applies directly on this one to rewrite these tests. If you want, you could rebase to have that test refactor happen before you add your new --ancestry-path=<X> option tests.
Thanks, I was going to comment on the same, but your solution is much better (I was just going to suggest using intermediate files).
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 60
Quoted 10 lines
[...]
t/t6019-rev-list-ancestry-path.sh +3
-6-test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2Maybe add &&-chaining here (we do it in some cases, but I'm not sure when such assignments would ever fail).
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 65
Quoted 5 lines
t/t6019-rev-list-ancestry-path.sh +5
-0+ test_expect_success "rev-list $args" " + test_write_lines $expected >expect && + git rev-list --format=%s $args >raw && + + if test -n \"$expected\"Aren't you making things harder for yourself here than required by using ""-quoting for the body of the test.
We eval it into existence, so you can use ''-quotes, and then you don't need to escape e.g. the "" quotes here for expected, no?
Quoted 6 lines
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 67
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ then + sed -e \"/^commit /d\" raw | sort >actual &&nit for debuggability (and not correctness), maybe using intermediate files here would be nicer? And then perhaps call them "actual" and "actual.sorted".
Would be better to just nuke the sed by replacing 'rev-list' with 'log' (the line already has a --format option, so might as well get the output we want).
Quoted 11 lines
Quoted 1 line
+
test_cmpexpect actual || return 1No need for a "return 1" here when we're not in a loop. It's redundant, and makes the -x output on failure confusing ("why didn't I fail on the
test_cmp, but on this stray return?...")....
t/t6019-rev-list-ancestry-path.sh test_expect_success setup ' · line 70
Quoted 2 lines
t/t6019-rev-list-ancestry-path.sh +2
-0+ else + test_must_be_empty raw
Quoted 6 lines
...which would also allow you to extract much of this if/else at the cost of not using
test_must_be_empty, i.e. just make the "expected" empty unless "$expected" is provided. Just a thought/nit, we could also leave this as-is :)Also does the "compare rev" part of this want
test_cmp_revinstead?
Um, I don't see any "compare rev" part of this, or any revision comparing. What are you referring to?
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 7 lines · Elijah Newren
Quoted 2 lines · Ævar Arnfjörð Bjarmason
[...]
Also does the "compare rev" part of this wanttest_cmp_revinstead?Um, I don't see any "compare rev" part of this, or any revision comparing. What are you referring to?
I just misread this part while skimming it, sorry.
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
In reply to Derrick StoleeQuoted 5 lines · Derrick Stolee
From: Derrick Stolee <[email protected]>
Subject: [PATCH] t6019: modernize tests with helperThe tests in t6019 are repetive, so create a helper that greatly simplifies the test script.
s/repetive/repetitive/
Quoted 6 lines
In addition, update the common pattern that places 'git rev-list' on the left side of a pipe, which can hide some exit codes. Send the output to a 'raw' file that is then consumed by other tools so the Git exit code is verified as zero.
Signed-off-by Derrick Stolee <[email protected]>
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
In reply to Derrick StoleeQuoted 34 lines · Derrick Stolee
Quoted 1 line · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
Code looks good!
Quoted 7 lines
t/t6019-rev-list-ancestry-path.sh +0
-0@@ -8,8 +8,13 @@ test_description='--ancestry-path' # / \ # A-------K---------------L--Mt/t6019-rev-list-ancestry-path.sh test_description='--ancestry-path' · line 17
Quoted 10 lines
t/t6019-rev-list-ancestry-path.sh +7
-2# -# D..M == E F G H I J K L M -# --ancestry-path D..M == E F H I J L M +# D..M == E F G H I J K L M +# --ancestry-path D..M == E F H I J L M +# --ancestry-path=F D..M == E F J L M +# --ancestry-path=G D..M == G H I J L M +# --ancestry-path=H D..M == E G H I J L M +# --ancestry-path=K D..M == K L M +# --ancestry-path=K --ancestry-path=F D..M == E F J K L MThese are good examples, because they help clarify what I had initially been confused about: you include things in _both_ directions. In particular, "--ancestry-path=K --ancestry-path=f D..M" you are kind of taking the union of the following queries:
--ancestry-path D..K
--ancestry-path K..M
--ancestry-path D..F
--ancestry-path F..M
Quoted 5 lines
I did check just in case, but specifying multiple ranges such as
--ancestry-path D..K K..Mdoes not do what is expected.
Right, because there's no such thing as multiple ranges. Quoting from the docs:
"""
Commands that are specifically designed to take two distinct ranges (e.g. "git range-diff R1 R2" to compare two ranges) do exist, but they are exceptions. Unless otherwise noted, all "git" commands that operate on a set of commits work on a single revision range. In other words, writing two "two-dot range notation" next to each other, e.g.
$ git log A..B C..D
does *not* specify two revision ranges for most commands. Instead it will name a single connected set of commits, i.e. those that are reachable from either B or D but are reachable from neither A or C. """
Quoted 9 lines
t/t6019-rev-list-ancestry-path.sh test_description='--ancestry-path'
Quoted 8 lines
t/t6019-rev-list-ancestry-path.sh +7
-0+test_expect_success 'rev-list --ancestry-path=F D..M' ' + test_write_lines E F J L M >expect && + git rev-list --ancestry-path=F --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +'
Quoted 6 lines
These tests follow the patterns from other tests in this file, but it also has bad patterns. Specifically, the 'git rev-list' command is fed directly into a pipe. I include a patch below that applies directly on this one to rewrite these tests. If you want, you could rebase to have that test refactor happen before you add your new --ancestry-path=<X> option tests.
Ooh, I like it. I'll rebase and include this patch earlier in my series. Thanks!
Quoted 189 lines
t/t6019-rev-list-ancestry-path.sh
Quoted 9 lines
t/t6019-rev-list-ancestry-path.sh +8
-0+test_expect_success 'rev-list --ancestry-path=G D..M' ' + test_write_lines G H I J L M >expect && + git rev-list --ancestry-path=G --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' +test_expect_success 'rev-list --ancestry-path=H D..M' 'nit: needs extra whitespace between tests. The above test pair needs one, too. This becomes moot with the patch I provide.
Thanks,
-Stolee--- >8 ---
From
9ac4e81dba0d7801513a09f5fe307d01357123ddMon Sep 17 00:00:00 2001
From: Derrick Stolee <[email protected]>
Date: Thu, 18 Aug 2022 11:25:04 -0400
Subject: [PATCH] t6019: modernize tests with helperThe tests in t6019 are repetive, so create a helper that greatly simplifies the test script.
In addition, update the common pattern that places 'git rev-list' on the left side of a pipe, which can hide some exit codes. Send the output to a 'raw' file that is then consumed by other tools so the Git exit code is verified as zero.
Signed-off-by Derrick Stolee <[email protected]>--- t/t6019-rev-list-ancestry-path.sh | 131 +++++++----------------------- 1 file changed, 31 insertions(+), 100 deletions(-)t/t6019-rev-list-ancestry-path.sh +31
-101@@ -55,111 +55,42 @@ test_expect_success setup ' test_commit M ' -test_expect_success 'rev-list D..M' ' - test_write_lines E F G H I J K L M >expect && - git rev-list --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M' ' - test_write_lines E F H I J L M >expect && - git rev-list --ancestry-path --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=F D..M' ' - test_write_lines E F J L M >expect && - git rev-list --ancestry-path=F --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' -test_expect_success 'rev-list --ancestry-path=G D..M' ' - test_write_lines G H I J L M >expect && - git rev-list --ancestry-path=G --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' -test_expect_success 'rev-list --ancestry-path=H D..M' ' - test_write_lines E G H I J L M >expect && - git rev-list --ancestry-path=H --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=K D..M' ' - test_write_lines K L M >expect && - git rev-list --ancestry-path=K --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path=F --ancestry-path=K D..M' ' - test_write_lines E F J K L M >expect && - git rev-list --ancestry-path=F --ancestry-path=K --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list D..M -- M.t' ' - echo M >expect && - git rev-list --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2 + test_expect_success "rev-list $args" " + test_write_lines $expected >expect && + git rev-list --format=%s $args >raw && + + if test -n \"$expected\" + then + sed -e \"/^commit /d\" raw | sort >actual && + test_cmp expect actual || return 1 + else + test_must_be_empty raw + fi + " +} -test_expect_success 'rev-list F...I' ' - test_write_lines F G H I >expect && - git rev-list --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "D..M" "E F G H I J K L M" -test_expect_success 'rev-list --ancestry-path F...I' ' - test_write_lines F H I >expect && - git rev-list --ancestry-path --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "--ancestry-path D..M" "E F H I J L M" +test_ancestry "--ancestry-path D..M" "E F H I J L M" +test_ancestry "--ancestry-path=F D..M" "E F J L M" +test_ancestry "--ancestry-path=G D..M" "G H I J L M" +test_ancestry "--ancestry-path=H D..M" "E G H I J L M" +test_ancestry "--ancestry-path=K D..M" "K L M" +test_ancestry "--ancestry-path=K --ancestry-path=F D..M" "E F J K L M" -# G.t is dropped in an "-s ours" merge -test_expect_success 'rev-list G..M -- G.t' ' - git rev-list --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_must_be_empty actual -' +test_ancestry "D..M -- M.t" "M" +test_ancestry "--ancestry-path D..M -- M.t" "M" -test_expect_success 'rev-list --ancestry-path G..M -- G.t' ' - echo L >expect && - git rev-list --ancestry-path --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry "F...I" "F G H I" +test_ancestry "--ancestry-path F...I" "F H I" -test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' ' - test_write_lines G L >expect && - git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "G..M -- G.t" "" +test_ancestry "--ancestry-path G..M -- G.t" "L" +test_ancestry "--ancestry-path --simplify-merges G^..M -- G.t" "G L" # b---bc # / \ / --2.37.1.vfs.0.0.rebase
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetThanks - overall this looks good. I only have some minor textual comments and one small code comment.
Quoted 7 lines · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path master..seenwhich shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor master
are not an ancestor *of* master
Quoted 12 lines
Documentation/rev-list-options.txt +1
-2@@ -389,12 +389,15 @@ Default mode:: merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path:: +--ancestry-path[=<commit>]:: When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), only display commits that exist
Documentation/rev-list-options.txt Default mode:: · line 400
Quoted 10 lines
Documentation/rev-list-options.txt +7
-3- directly on the ancestry chain between the 'commit1' and - 'commit2', i.e. commits that are both descendants of 'commit1', - and ancestors of 'commit2'. + or 'commit2 {caret}commit1'), only display commits in that + range where <commit> is part of the ancestry chain. By "part of + the ancestry chain", we mean including <commit> itself and + commits that are either ancestors or descendants of <commit>. + If no commit is specified, use 'commit1' (the excluded part of + the range) as <commit>. Can be passed multiple times to look for + commits in the ancestry range of multiple commits.
"Ancestry chain" seems to be used multiple times in the Git codebase, apparently with slightly different definitions, so probably best to clear up at least this part by not reusing the term. It's also probably not worth introducing a new term "ancestry range". Maybe rewrite to say:
When given a range of commits to display (e.g. 'commit1..commit2' or 'commit2 {caret}commit1'), only display commits in that range that are ancestors of <commit>, descendants of <commit>, or <commit> itself. If no commit is specified, use 'commit1' (the excluded part of the range) as <commit>. Can be passed multiple times; if so, a commit is included if it is any of the commits given or if it is an ancestor or descendant of one of them.
Documentation/rev-list-options.txt Note the major differences in `N`, `P`, and `Q` over `--full-history`: · line 577
Quoted 13 lines
Documentation/rev-list-options.txt +4
-5@@ -568,11 +571,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available: ---ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the ``from'' and ``to'' commits in the given commit - range. I.e. only display commits that are ancestor of the ``to'' - commit and descendants of the ``from'' commit. +--ancestry-path[=<commit>]:: + Limit the displayed commits to those containing <commit> in their + ancestry path. I.e. only display <commit> and commits which have + <commit> as either a direct ancestor or descendant.
Can we refer back to the documentation of --ancestry-path instead?
Quoted 13 lines
Documentation/rev-list-options.txt +4
-4@@ -1304,13 +1304,20 @@ static int still_interesting(struct commit_list *src, timestamp_t date, int slop } /* - * "rev-list --ancestry-path A..B" computes commits that are ancestors + * "rev-list --ancestry-path=C A..B" computes commits that are ancestors * of B but not ancestors of A but further limits the result to those - * that are descendants of A. This takes the list of bottom commits and - * the result of "A..B" without --ancestry-path, and limits the latter - * further to the ones that can reach one of the commits in "bottom". + * that have C in their ancestry path (i.e. are either ancestors of C, + * descendants of C, or are C). If multiple --ancestry-path=$COMMITTISH + * arguments are supplied, we limit the result to those that have at
Documentation/rev-list-options.txt static int still_interesting(struct commit_list *src, timestamp_t date, int slop · line 1320
Quoted 10 lines
Documentation/rev-list-options.txt +8
-1+ * least one of those COMMITTISH in their ancestry path. If + * --ancestry-path is specified with no commit, we use all bottom + * commits for C. + * + * Before this function is called, ancestors of C will have already been + * marked with ANCESTRY_PATH previously, so we just need to also mark + * the descendants here, then collect both sets of commits. */ -static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list) +static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)
I thought the original description of this function ("This takes the list...") to be clear and it would be nice to retain it. So, e.g.:
"rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B" computes commits that are ancestors of B but not ancestors of A but further limits the result to those that have any of C in their ancestry path (i.e. are either ancestors of any of C, descendants of any of C, or are any of C). If --ancestry-path is specified with no commit, we use all bottom commits for C.
Before this function is called, ancestors of C will have already been marked with ANCESTRY_PATH previously.
This takes the list of bottom commits and the result of "A..B" without --ancestry-path, and limits the latter further to the ones that have any of C in their ancestry path. Since the ancestors of C have already been marked (a prerequisite of this function), we just need to mark the descendants, then exclude any commit that does not have any of these marks.
Optional: Besides that, from what I can tell, sometimes the C commits themselves are marked with ANCESTRY_PATH (when they are explicitly specified) and sometimes they are not (when they are not explicitly specified). It's not a bug here, but it might be worth handling that in the ancestry_path_need_bottoms codepath (instead of explicitly setting TMP_MARK on the bottoms in limit_to_ancestry() - that way, I think we can also use ANCESTRY_PATH instead of TMP_MARK throughout the ancestry path codepaths, but I haven't tested it), at least to prevent possible future bugs.
Quoted 8 lines
Documentation/rev-list-options.txt +1
-1@@ -2213,7 +2220,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg const struct setup_revision_opt* opt) { const char *arg = argv[0]; - const char *optarg; + const char *optarg = NULL; int argcount; const unsigned hexsz = the_hash_algo->hexsz;
[snip]
Documentation/rev-list-options.txt static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg · line 2291
Quoted 7 lines
Documentation/rev-list-options.txt +2
-1@@ -2280,10 +2287,26 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->first_parent_only = 1; } else if (!strcmp(arg, "--exclude-first-parent-only")) { revs->exclude_first_parent_only = 1; - } else if (!strcmp(arg, "--ancestry-path")) { + } else if (!strcmp(arg, "--ancestry-path") || + skip_prefix(arg, "--ancestry-path=", &optarg)) {
This and the above hunk might cause bugs if --ancestry-path was first specified with a commit and then specified without. Probably best to break this up into separate "else if" branches, even though there is a bit of code duplication (and also remove the "= NULL" addition in the above hunk).
Documentation/rev-list-options.txt struct rev_info { · line 168
Quoted 5 lines
Documentation/rev-list-options.txt +1
-0@@ -164,6 +165,7 @@ struct rev_info { cherry_mark:1, bisect:1, ancestry_path:1, + ancestry_path_need_bottoms:1,
Might be better named as ancestry_path_implicit_bottoms? And probably worth documenting, e.g.
True if --ancestry-path was specified without an argument. The bottom revisions are implicitly the arguments in this case.
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 7 lines · Jonathan Tan
Thanks - overall this looks good. I only have some minor textual comments and one small code comment.
Quoted 2 lines · Elijah Newren via GitGitGadget
From: Elijah Newren <[email protected]>
Quoted 7 lines
Quoted 5 lines
We have long allowed users to run e.g.
git log --ancestry-path master..seenwhich shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor masterare not an ancestor *of* master
Thanks, good catch.
Quoted 29 lines
Quoted 12 lines
Documentation/rev-list-options.txt +1
-2@@ -389,12 +389,15 @@ Default mode:: merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path:: +--ancestry-path[=<commit>]:: When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), only display commits that existDocumentation/rev-list-options.txt Default mode:: · line 400
Quoted 10 lines
Documentation/rev-list-options.txt +7
-3- directly on the ancestry chain between the 'commit1' and - 'commit2', i.e. commits that are both descendants of 'commit1', - and ancestors of 'commit2'. + or 'commit2 {caret}commit1'), only display commits in that + range where <commit> is part of the ancestry chain. By "part of + the ancestry chain", we mean including <commit> itself and + commits that are either ancestors or descendants of <commit>. + If no commit is specified, use 'commit1' (the excluded part of + the range) as <commit>. Can be passed multiple times to look for + commits in the ancestry range of multiple commits."Ancestry chain" seems to be used multiple times in the Git codebase, apparently with slightly different definitions, so probably best to clear up at least this part by not reusing the term. It's also probably not worth introducing a new term "ancestry range". Maybe rewrite to say:
Quoted 7 lines
When given a range of commits to display (e.g. 'commit1..commit2' or 'commit2 {caret}commit1'), only display commits in that range that are ancestors of <commit>, descendants of <commit>, or <commit> itself. If no commit is specified, use 'commit1' (the excluded part of the range) as <commit>. Can be passed multiple times; if so, a commit is included if it is any of the commits given or if it is an ancestor or descendant of one of them.
Works for me; thanks.
Quoted 4 lines
Quoted 4 lines
Documentation/rev-list-options.txt +0
-0@@ -568,11 +571,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available:
Quoted 11 lines
Documentation/rev-list-options.txt Note the major differences in `N`, `P`, and `Q` over `--full-history`: · line 577
Quoted 9 lines
Documentation/rev-list-options.txt +4
-5---ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the ``from'' and ``to'' commits in the given commit - range. I.e. only display commits that are ancestor of the ``to'' - commit and descendants of the ``from'' commit. +--ancestry-path[=<commit>]:: + Limit the displayed commits to those containing <commit> in their + ancestry path. I.e. only display <commit> and commits which have + <commit> as either a direct ancestor or descendant.Can we refer back to the documentation of --ancestry-path instead?
I had the same thought, especially since the nearby text also duplicates definitions for --dense, --sparse, --simplify-merges, and --show-pulls, each of which might also benefit from just referring back to previous definitions to avoid drift. I think we should handle this whole "History Simplification" section consistently, so if we just refer back to the previous definition somehow for this flag then we should also do the same for the others. But some of the others appear to intentionally avoid using the same wording in order to draw graphs and pictorially explain it.
So it feels like a can of worms that I'm not sure how to solve, and what I currently have is the best solution available.
Quoted 37 lines
Quoted 13 lines
Documentation/rev-list-options.txt +4
-4@@ -1304,13 +1304,20 @@ static int still_interesting(struct commit_list *src, timestamp_t date, int slop } /* - * "rev-list --ancestry-path A..B" computes commits that are ancestors + * "rev-list --ancestry-path=C A..B" computes commits that are ancestors * of B but not ancestors of A but further limits the result to those - * that are descendants of A. This takes the list of bottom commits and - * the result of "A..B" without --ancestry-path, and limits the latter - * further to the ones that can reach one of the commits in "bottom". + * that have C in their ancestry path (i.e. are either ancestors of C, + * descendants of C, or are C). If multiple --ancestry-path=$COMMITTISH + * arguments are supplied, we limit the result to those that have atDocumentation/rev-list-options.txt static int still_interesting(struct commit_list *src, timestamp_t date, int slop · line 1320
Quoted 10 lines
Documentation/rev-list-options.txt +8
-1+ * least one of those COMMITTISH in their ancestry path. If + * --ancestry-path is specified with no commit, we use all bottom + * commits for C. + * + * Before this function is called, ancestors of C will have already been + * marked with ANCESTRY_PATH previously, so we just need to also mark + * the descendants here, then collect both sets of commits. */ -static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list) +static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)I thought the original description of this function ("This takes the list...") to be clear and it would be nice to retain it. So, e.g.:
"rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B" computes commits that are ancestors of B but not ancestors of A but further limits the result to those that have any of C in their ancestry path (i.e. are either ancestors of any of C, descendants of any of C, or are any of C). If --ancestry-path is specified with no commit, we use all bottom commits for C.
Before this function is called, ancestors of C will have already been marked with
ANCESTRY_PATHpreviously.
Quoted 6 lines
This takes the list of bottom commits and the result of "A..B" without --ancestry-path, and limits the latter further to the ones that have any of C in their ancestry path. Since the ancestors of C have already been marked (a prerequisite of this function), we just need to mark the descendants, then exclude any commit that does not have any of these marks.
Sounds good to me; I'm happy to adopt this wording.
Quoted 9 lines
Optional: Besides that, from what I can tell, sometimes the C commits themselves are marked with
ANCESTRY_PATH(when they are explicitly specified) and sometimes they are not (when they are not explicitly specified). It's not a bug here, but it might be worth handling that in theancestry_path_need_bottomscodepath (instead of explicitly settingTMP_MARKon the bottoms inlimit_to_ancestry()- that way, I think we can also useANCESTRY_PATHinstead ofTMP_MARKthroughout the ancestry path codepaths, but I haven't tested it), at least to prevent possible future bugs.
That sounds like you're trying to duplicate the bug in my first attempt at this patch. If you try to coalesce ANCESTRY_PATH and TMP_MARK, then you not only get all descendants of C, you also get all descendants of any ancestor of C, which defeats the whole point of my changes.
It's true that I don't mark implicit C commits with ANCESTRY_PATH, but those are always bottom commits that are the excluded end of a range anyway. While those could be marked without causing problems, it would always be a waste of effort.
Quoted 11 lines
Quoted 8 lines
Documentation/rev-list-options.txt +1
-1@@ -2213,7 +2220,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg const struct setup_revision_opt* opt) { const char *arg = argv[0]; - const char *optarg; + const char *optarg = NULL; int argcount; const unsigned hexsz = the_hash_algo->hexsz;[snip]
Quoted 13 lines
Documentation/rev-list-options.txt static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg · line 2291
Quoted 7 lines
Documentation/rev-list-options.txt +2
-1@@ -2280,10 +2287,26 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->first_parent_only = 1; } else if (!strcmp(arg, "--exclude-first-parent-only")) { revs->exclude_first_parent_only = 1; - } else if (!strcmp(arg, "--ancestry-path")) { + } else if (!strcmp(arg, "--ancestry-path") || + skip_prefix(arg, "--ancestry-path=", &optarg)) {This and the above hunk might cause bugs if --ancestry-path was first specified with a commit and then specified without. Probably best to break this up into separate "else if" branches, even though there is a bit of code duplication (and also remove the "= NULL" addition in the above hunk).
Ah, good catch.
Quoted 11 lines
Documentation/rev-list-options.txt struct rev_info { · line 168
Quoted 5 lines
Documentation/rev-list-options.txt +1
-0@@ -164,6 +165,7 @@ struct rev_info { cherry_mark:1, bisect:1, ancestry_path:1, + ancestry_path_need_bottoms:1,Might be better named as
ancestry_path_implicit_bottoms? And probably worth documenting, e.g.True if --ancestry-path was specified without an argument. The bottom revisions are implicitly the arguments in this case.
Sure, sounds good.
Thanks for the careful review!
Re: [PATCH v2 2/2] revision: allow --ancestry-path to take an argument
Quoted 10 lines · Elijah Newren
Quoted 9 lines
Optional: Besides that, from what I can tell, sometimes the C commits themselves are marked with
ANCESTRY_PATH(when they are explicitly specified) and sometimes they are not (when they are not explicitly specified). It's not a bug here, but it might be worth handling that in theancestry_path_need_bottomscodepath (instead of explicitly settingTMP_MARKon the bottoms inlimit_to_ancestry()- that way, I think we can also useANCESTRY_PATHinstead ofTMP_MARKthroughout the ancestry path codepaths, but I haven't tested it), at least to prevent possible future bugs.
Quoted 5 lines
That sounds like you're trying to duplicate the bug in my first attempt at this patch. If you try to coalesce
ANCESTRY_PATHandTMP_MARK, then you not only get all descendants of C, you also get all descendants of any ancestor of C, which defeats the whole point of my changes.
Ah, yes you're right.
Quoted 4 lines
It's true that I don't mark implicit C commits with
ANCESTRY_PATH, but those are always bottom commits that are the excluded end of a range anyway. While those could be marked without causing problems, it would always be a waste of effort.
Yes, that's true.
Quoted 4 lines · Elijah Newren via GitGitGadget
Changes since v1:
* Tweaked the commit message, and incorporated Junio's suggestion to update
left_flagandancestry_flagtogether.
Thanks, will replace and queue.
v3 cover Allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetChanges since v2:
* Incorporated Stolee's suggested patch as a preliminary cleanup of t6019. Slightly modified his patch by:
* fixing the "repetitive" typo pointed out by Eric
* removed the unnecessary "return 1" pointed out by Ævar
* switched "rev-list" to "log" since we are using --format anyway, in order to remove the need to call "sed" afterward
* lots of wording improvements suggested by Jonathan
* fixed an issue with argument parsing pointed out by Jonathan
Changes since v1:
* Tweaked the commit message, and incorporated Junio's suggestion to update left_flag and ancestry_flag together.
Series description:
This came out of a previous thread[1], where I wanted to be able to run something like
git log --oneline --ancestry-path=ab/submodule-cleanup main..seen
and see the commits in main..seen which contained ab/submodule-cleanup in their ancestry path. Let me start by defining the terminology "X is in a commit's ancestry path". By that, I just mean that either the commit is X, the commit is an ancestor of X, or the commit is a descendant of X. With that definition...
The command
git log --ancestry-path A..B
means find the commits in A..B which contain A in their ancestry path. I sometimes still want to use A..B to get the basic range, but would like to use a commit other than A for specifying which ancestry path is of interest. So, for example, I might want to use
git log --ancestry-path=C A..B
to mean find the commits in A..B which contain C in their ancestry path, or use
git log --ancestry-path=C --ancestry-path=D A..B
to mean find the commits in A..B which contain either C or D in their ancestry path.
This series implements this request, by allowing --ancestry-path to take an optional argument. With it, I can find the answer to my question in the thread at [1] within the git.git repository (replacing branch names with actual hashes since the branches have since moved on):
$ git log --oneline --ancestry-path=5b893f7d81 8168d5e9c2..ac0248bfba | wc -l 36
This returns the answer I want, whereas dropping the '=5b893f7d81' from the command line gives me 192 unwanted commits (228 total), and various other command line flags (--first-parent, --boundary, etc.) also fail to give me the set of commits I am looking for.
[1]
https://lore.kernel.org/git/CABPp-BF+8aqysioP_e27Q9kJ02rE2SuSqXu+XphzKWnk5a_Q+A@mail.gmail.com/
Derrick Stolee (1):
t6019: modernize tests with helper
Elijah Newren (2):
rev-list-options.txt: fix simple typo
revision: allow --ancestry-path to take an argument
Documentation/rev-list-options.txt | 46 +++++++++---- object.h | 2 +- revision.c | 89 ++++++++++++++++--------- revision.h | 9 +++ t/t6019-rev-list-ancestry-path.sh | 101 +++++++++++------------------ 5 files changed, 141 insertions(+), 106 deletions(-)
base-commit: 6a475b71f8c4ce708d69fdc9317aefbde3769e25
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1303%2Fnewren%2Fancestry-path-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1303/newren/ancestry-path-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1303
Range-diff vs v2:
1: 68ab719d99c = 1: 68ab719d99c rev-list-options.txt: fix simple typo
-: ----------- > 2: 5226da2123e t6019: modernize tests with helper
2: f580ec6d060 ! 3: b810b3c8a2a revision: allow --ancestry-path to take an argument
@@ Commit message
git log --ancestry-path master..seen
which shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
- * are not an ancestor master
+ * are not an ancestor of master
* have master as an ancestor
This commit allows another variant:
@@ Documentation/rev-list-options.txt: Default mode::
- directly on the ancestry chain between the 'commit1' and
- 'commit2', i.e. commits that are both descendants of 'commit1',
- and ancestors of 'commit2'.
-+ or 'commit2 {caret}commit1'), only display commits in that
-+ range where <commit> is part of the ancestry chain. By "part of
-+ the ancestry chain", we mean including <commit> itself and
-+ commits that are either ancestors or descendants of <commit>.
-+ If no commit is specified, use 'commit1' (the excluded part of
-+ the range) as <commit>. Can be passed multiple times to look for
-+ commits in the ancestry range of multiple commits.
++ or 'commit2 {caret}commit1'), only display commits in that range
++ that are ancestors of <commit>, descendants of <commit>, or
++ <commit> itself. If no commit is specified, use 'commit1' (the
++ excluded part of the range) as <commit>. Can be passed multiple
++ times; if so, a commit is included if it is any of the commits
++ given or if it is an ancestor or descendant of one of them.
A more detailed explanation follows.
@@ Documentation/rev-list-options.txt: Note the major differences in `N`, `P`, and
- range. I.e. only display commits that are ancestor of the ``to''
- commit and descendants of the ``from'' commit.
+--ancestry-path[=<commit>]::
-+ Limit the displayed commits to those containing <commit> in their
-+ ancestry path. I.e. only display <commit> and commits which have
-+ <commit> as either a direct ancestor or descendant.
++ Limit the displayed commits to those which are an ancestor of
++ <commit>, or which are a descendant of <commit>, or are <commit>
++ itself.
+
As an example use case, consider the following commit history:
+
@@ revision.c: static int still_interesting(struct commit_list *src, timestamp_t da
/*
- * "rev-list --ancestry-path A..B" computes commits that are ancestors
-+ * "rev-list --ancestry-path=C A..B" computes commits that are ancestors
- * of B but not ancestors of A but further limits the result to those
+- * of B but not ancestors of A but further limits the result to those
- * that are descendants of A. This takes the list of bottom commits and
- * the result of "A..B" without --ancestry-path, and limits the latter
- * further to the ones that can reach one of the commits in "bottom".
-+ * that have C in their ancestry path (i.e. are either ancestors of C,
-+ * descendants of C, or are C). If multiple --ancestry-path=$COMMITTISH
-+ * arguments are supplied, we limit the result to those that have at
-+ * least one of those COMMITTISH in their ancestry path. If
-+ * --ancestry-path is specified with no commit, we use all bottom
-+ * commits for C.
++ * "rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B"
++ * computes commits that are ancestors of B but not ancestors of A but
++ * further limits the result to those that have any of C in their
++ * ancestry path (i.e. are either ancestors of any of C, descendants
++ * of any of C, or are any of C). If --ancestry-path is specified with
++ * no commit, we use all bottom commits for C.
++ *
++ * Before this function is called, ancestors of C will have already
++ * been marked with ANCESTRY_PATH previously.
+ *
-+ * Before this function is called, ancestors of C will have already been
-+ * marked with ANCESTRY_PATH previously, so we just need to also mark
-+ * the descendants here, then collect both sets of commits.
++ * This takes the list of bottom commits and the result of "A..B"
++ * without --ancestry-path, and limits the latter further to the ones
++ * that have any of C in their ancestry path. Since the ancestors of C
++ * have already been marked (a prerequisite of this function), we just
++ * need to mark the descendants, then exclude any commit that does not
++ * have any of these marks.
*/
-static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)
@@ revision.c: static int limit_list(struct rev_info *revs)
- if (revs->ancestry_path) {
- bottom = collect_bottom_commits(original_list);
- if (!bottom)
-+ if (revs->ancestry_path_need_bottoms) {
++ if (revs->ancestry_path_implicit_bottoms) {
+ collect_bottom_commits(original_list,
+ &revs->ancestry_path_bottoms);
+ if (!revs->ancestry_path_bottoms)
@@ revision.c: static int handle_revision_opt(struct rev_info *revs, int argc, cons
const unsigned hexsz = the_hash_algo->hexsz;
@@ revision.c: static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
- revs->first_parent_only = 1;
- } else if (!strcmp(arg, "--exclude-first-parent-only")) {
- revs->exclude_first_parent_only = 1;
-- } else if (!strcmp(arg, "--ancestry-path")) {
-+ } else if (!strcmp(arg, "--ancestry-path") ||
-+ skip_prefix(arg, "--ancestry-path=", &optarg)) {
revs->ancestry_path = 1;
revs->simplify_history = 0;
revs->limited = 1;
-+ if (optarg) {
-+ struct commit *c;
-+ struct object_id oid;
-+ const char *msg = _("could not get commit for ancestry-path argument %s");
++ revs->ancestry_path_implicit_bottoms = 1;
++ } else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
++ struct commit *c;
++ struct object_id oid;
++ const char *msg = _("could not get commit for ancestry-path argument %s");
++
++ revs->ancestry_path = 1;
++ revs->simplify_history = 0;
++ revs->limited = 1;
+
-+ if (repo_get_oid_committish(revs->repo, optarg, &oid))
-+ return error(msg, optarg);
-+ get_reference(revs, optarg, &oid, ANCESTRY_PATH);
-+ c = lookup_commit_reference(revs->repo, &oid);
-+ if (!c)
-+ return error(msg, optarg);
-+ commit_list_insert(c, &revs->ancestry_path_bottoms);
-+ } else {
-+ revs->ancestry_path_need_bottoms = 1;
-+ }
++ if (repo_get_oid_committish(revs->repo, optarg, &oid))
++ return error(msg, optarg);
++ get_reference(revs, optarg, &oid, ANCESTRY_PATH);
++ c = lookup_commit_reference(revs->repo, &oid);
++ if (!c)
++ return error(msg, optarg);
++ commit_list_insert(c, &revs->ancestry_path_bottoms);
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
init_reflog_walk(&revs->reflog_info);
} else if (!strcmp(arg, "--default")) {
@@ revision.h: struct rev_info {
cherry_mark:1,
bisect:1,
ancestry_path:1,
-+ ancestry_path_need_bottoms:1,
++
++ /* True if --ancestry-path was specified without an
++ * argument. The bottom revisions are implicitly
++ * the arguments in this case.
++ */
++ ancestry_path_implicit_bottoms:1,
++
first_parent_only:1,
exclude_first_parent_only:1,
line_level_traverse:1,
@@ t/t6019-rev-list-ancestry-path.sh: test_description='--ancestry-path'
#
# D..M -- M.t == M
# --ancestry-path D..M -- M.t == M
-@@ t/t6019-rev-list-ancestry-path.sh: test_expect_success 'rev-list --ancestry-path D..M' '
- test_cmp expect actual
- '
+@@ t/t6019-rev-list-ancestry-path.sh: test_ancestry () {
+ test_ancestry "D..M" "E F G H I J K L M"
-+test_expect_success 'rev-list --ancestry-path=F D..M' '
-+ test_write_lines E F J L M >expect &&
-+ git rev-list --ancestry-path=F --format=%s D..M |
-+ sed -e "/^commit /d" |
-+ sort >actual &&
-+ test_cmp expect actual
-+'
-+test_expect_success 'rev-list --ancestry-path=G D..M' '
-+ test_write_lines G H I J L M >expect &&
-+ git rev-list --ancestry-path=G --format=%s D..M |
-+ sed -e "/^commit /d" |
-+ sort >actual &&
-+ test_cmp expect actual
-+'
-+test_expect_success 'rev-list --ancestry-path=H D..M' '
-+ test_write_lines E G H I J L M >expect &&
-+ git rev-list --ancestry-path=H --format=%s D..M |
-+ sed -e "/^commit /d" |
-+ sort >actual &&
-+ test_cmp expect actual
-+'
-+
-+test_expect_success 'rev-list --ancestry-path=K D..M' '
-+ test_write_lines K L M >expect &&
-+ git rev-list --ancestry-path=K --format=%s D..M |
-+ sed -e "/^commit /d" |
-+ sort >actual &&
-+ test_cmp expect actual
-+'
-+
-+test_expect_success 'rev-list --ancestry-path=F --ancestry-path=K D..M' '
-+ test_write_lines E F J K L M >expect &&
-+ git rev-list --ancestry-path=F --ancestry-path=K --format=%s D..M |
-+ sed -e "/^commit /d" |
-+ sort >actual &&
-+ test_cmp expect actual
-+'
-+
- test_expect_success 'rev-list D..M -- M.t' '
- echo M >expect &&
- git rev-list --format=%s D..M -- M.t |
+ test_ancestry "--ancestry-path D..M" "E F H I J L M"
++test_ancestry "--ancestry-path=F D..M" "E F J L M"
++test_ancestry "--ancestry-path=G D..M" "G H I J L M"
++test_ancestry "--ancestry-path=H D..M" "E G H I J L M"
++test_ancestry "--ancestry-path=K D..M" "K L M"
++test_ancestry "--ancestry-path=F --ancestry-path=K D..M" "E F J K L M"
+
+ test_ancestry "D..M -- M.t" "M"
+ test_ancestry "--ancestry-path D..M -- M.t" "M"Signature · 1 line
gitgitgadget
PATCH v3 1/3 rev-list-options.txt: fix simple typo
From: Elijah Newren <[email protected]>
--- Documentation/rev-list-options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/rev-list-options.txt +1 -1
@@ -659,7 +659,7 @@ Here, the merge commits `O` and `P` contribute extra noise, as they did not actually contribute a change to `file.txt`. They only merged a topic that was based on an older version of `file.txt`. This is a common issue in repositories using a workflow where many contributors work in -parallel and merge their topic branches along a single trunk: manu +parallel and merge their topic branches along a single trunk: many unrelated merges appear in the `--full-history` results. When using the `--simplify-merges` option, the commits `O` and `P`
Signature · 1 line
gitgitgadget
PATCH v3 2/3 t6019: modernize tests with helper
In reply to Elijah Newren via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The tests in t6019 are repetitive, so create a helper that greatly simplifies the test script.
In addition, update the common pattern that places 'git rev-list' on the left side of a pipe, which can hide some exit codes. Send the output to a 'raw' file that is then consumed by other tools so the Git exit code is verified as zero. And since we're using --format anyway, switch to git log, so that we get the desired format and can avoid using sed.
--- t/t6019-rev-list-ancestry-path.sh | 87 +++++++++---------------------- 1 file changed, 25 insertions(+), 62 deletions(-)
t/t6019-rev-list-ancestry-path.sh +25 -62
@@ -50,73 +50,36 @@ test_expect_success setup ' test_commit M ' -test_expect_success 'rev-list D..M' ' - test_write_lines E F G H I J K L M >expect && - git rev-list --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M' ' - test_write_lines E F H I J L M >expect && - git rev-list --ancestry-path --format=%s D..M | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list D..M -- M.t' ' - echo M >expect && - git rev-list --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' - -test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' - echo M >expect && - git rev-list --ancestry-path --format=%s D..M -- M.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry () { + args=$1 + expected=$2 + test_expect_success "log $args" " + test_write_lines $expected >expect && + git log --format=%s $args >raw && + + if test -n \"$expected\" + then + sort raw >actual && + test_cmp expect actual + else + test_must_be_empty raw + fi + " +} -test_expect_success 'rev-list F...I' ' - test_write_lines F G H I >expect && - git rev-list --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "D..M" "E F G H I J K L M" -test_expect_success 'rev-list --ancestry-path F...I' ' - test_write_lines F H I >expect && - git rev-list --ancestry-path --format=%s F...I | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "--ancestry-path D..M" "E F H I J L M" -# G.t is dropped in an "-s ours" merge -test_expect_success 'rev-list G..M -- G.t' ' - git rev-list --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_must_be_empty actual -' +test_ancestry "D..M -- M.t" "M" +test_ancestry "--ancestry-path D..M -- M.t" "M" -test_expect_success 'rev-list --ancestry-path G..M -- G.t' ' - echo L >expect && - git rev-list --ancestry-path --format=%s G..M -- G.t | - sed -e "/^commit /d" >actual && - test_cmp expect actual -' +test_ancestry "F...I" "F G H I" +test_ancestry "--ancestry-path F...I" "F H I" -test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' ' - test_write_lines G L >expect && - git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t | - sed -e "/^commit /d" | - sort >actual && - test_cmp expect actual -' +test_ancestry "G..M -- G.t" "" +test_ancestry "--ancestry-path G..M -- G.t" "L" +test_ancestry "--ancestry-path --simplify-merges G^..M -- G.t" "G L" # b---bc # / \ /
Signature · 1 line
gitgitgadget
PATCH v3 3/3 revision: allow --ancestry-path to take an argument
In reply to Elijah Newren via GitGitGadgetFrom: Elijah Newren <[email protected]>
We have long allowed users to run e.g.
git log --ancestry-path master..seen
which shows all commits which satisfy all three of these criteria:
* are an ancestor of seen
* are not an ancestor of master
* have master as an ancestor
This commit allows another variant:
git log --ancestry-path=$TOPIC master..seen
which shows all commits which satisfy all of these criteria:
* are an ancestor of seen
* are not an ancestor of master
* have $TOPIC in their ancestry-path
that last bullet can be defined as commits meeting any of these criteria:
* are an ancestor of $TOPIC
* have $TOPIC as an ancestor
* are $TOPIC
This also allows multiple --ancestry-path arguments, which can be used to find commits with any of the given topics in their ancestry path.
--- Documentation/rev-list-options.txt | 44 +++++++++++---- object.h | 2 +- revision.c | 89 ++++++++++++++++++++---------- revision.h | 9 +++ t/t6019-rev-list-ancestry-path.sh | 14 ++++- 5 files changed, 115 insertions(+), 43 deletions(-)
Documentation/rev-list-options.txt +34 -10
@@ -389,12 +389,14 @@ Default mode:: merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path:: +--ancestry-path[=<commit>]:: When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), only display commits that exist - directly on the ancestry chain between the 'commit1' and - 'commit2', i.e. commits that are both descendants of 'commit1', - and ancestors of 'commit2'. + or 'commit2 {caret}commit1'), only display commits in that range + that are ancestors of <commit>, descendants of <commit>, or + <commit> itself. If no commit is specified, use 'commit1' (the + excluded part of the range) as <commit>. Can be passed multiple + times; if so, a commit is included if it is any of the commits + given or if it is an ancestor or descendant of one of them. A more detailed explanation follows.
@@ -568,11 +570,10 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available: ---ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the ``from'' and ``to'' commits in the given commit - range. I.e. only display commits that are ancestor of the ``to'' - commit and descendants of the ``from'' commit. +--ancestry-path[=<commit>]:: + Limit the displayed commits to those which are an ancestor of + <commit>, or which are a descendant of <commit>, or are <commit> + itself. + As an example use case, consider the following commit history: +
@@ -604,6 +605,29 @@ option does. Applied to the 'D..M' range, it results in: \ L--M ----------------------------------------------------------------------- ++ +We can also use `--ancestry-path=D` instead of `--ancestry-path` which +means the same thing when applied to the 'D..M' range but is just more +explicit. ++ +If we instead are interested in a given topic within this range, and all +commits affected by that topic, we may only want to view the subset of +`D..M` which contain that topic in their ancestry path. So, using +`--ancestry-path=H D..M` for example would result in: ++ +----------------------------------------------------------------------- + E + \ + G---H---I---J + \ + L--M +----------------------------------------------------------------------- ++ +Whereas `--ancestry-path=K D..M` would result in ++ +----------------------------------------------------------------------- + K---------------L--M +----------------------------------------------------------------------- Before discussing another option, `--show-pulls`, we need to create a new example history.
object.h +1 -1
@@ -59,7 +59,7 @@ struct object_array {/* * object flag allocation: -* revision.h: 0---------10 15 23------26 +* revision.h: 0---------10 15 23------27 * fetch-pack.c: 01 67 * negotiator/default.c: 2--5 * walker.c: 0-2
revision.c +59 -30
178 lines not on this page · raw message
revision.h +9 -0
@@ -48,6 +48,7 @@*/ #define NOT_USER_GIVEN (1u<<25)#define TRACK_LINEAR (1u<<26) +#define ANCESTRY_PATH (1u<<27)#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)#define DECORATE_SHORT_REFS 1
@@ -164,6 +165,13 @@ struct rev_info { cherry_mark:1, bisect:1, ancestry_path:1, + + /* True if --ancestry-path was specified without an + * argument. The bottom revisions are implicitly + * the arguments in this case. + */ + ancestry_path_implicit_bottoms:1, + first_parent_only:1, exclude_first_parent_only:1, line_level_traverse:1,
@@ -306,6 +314,7 @@ struct rev_info {struct saved_parents *saved_parents_slab;struct commit_list *previous_parents; +struct commit_list *ancestry_path_bottoms;const char *break_bar;struct revision_sources *sources;
t/t6019-rev-list-ancestry-path.sh +12 -2
@@ -8,8 +8,13 @@ test_description='--ancestry-path' # / \ # A-------K---------------L--M # -# D..M == E F G H I J K L M -# --ancestry-path D..M == E F H I J L M +# D..M == E F G H I J K L M +# --ancestry-path D..M == E F H I J L M +# --ancestry-path=F D..M == E F J L M +# --ancestry-path=G D..M == G H I J L M +# --ancestry-path=H D..M == E G H I J L M +# --ancestry-path=K D..M == K L M +# --ancestry-path=K --ancestry-path=F D..M == E F J K L M # # D..M -- M.t == M # --ancestry-path D..M -- M.t == M
@@ -70,6 +75,11 @@ test_ancestry () { test_ancestry "D..M" "E F G H I J K L M" test_ancestry "--ancestry-path D..M" "E F H I J L M" +test_ancestry "--ancestry-path=F D..M" "E F J L M" +test_ancestry "--ancestry-path=G D..M" "G H I J L M" +test_ancestry "--ancestry-path=H D..M" "E G H I J L M" +test_ancestry "--ancestry-path=K D..M" "K L M" +test_ancestry "--ancestry-path=F --ancestry-path=K D..M" "E F J K L M" test_ancestry "D..M -- M.t" "M" test_ancestry "--ancestry-path D..M -- M.t" "M"
Signature · 1 line
gitgitgadget
Re: [PATCH v3 3/3] revision: allow --ancestry-path to take an argument
revision.h struct rev_info { · line 173
Quoted 7 lines · Elijah Newren via GitGitGadget
revision.h +6
-0ancestry_path:1 , + +/* True if --ancestry-path was specified without an +* argument. The bottom revisions are implicitly +* the arguments in this case. +*/ + ancestry_path_implicit_bottoms:1,
The above comment explains why this is called "implicit" very well. The earlier round used "need", but the word missed the essense (i.e. ancestry_path argument is implicit and not given, so we need to compute bottoms and use them instead). The new name is certainly better.
Re: [PATCH v3 3/3] revision: allow --ancestry-path to take an argument
Quoted 11 lines · Junio C Hamano
revision.h struct rev_info { · line 173
Quoted 7 lines · Elijah Newren via GitGitGadget
ancestry_path:1,
revision.h +6
-0+ +/* True if --ancestry-path was specified without an +* argument. The bottom revisions are implicitly +* the arguments in this case. +*/ + ancestry_path_implicit_bottoms:1,
Quoted 5 lines
The above comment explains why this is called "implicit" very well. The earlier round used "need", but the word missed the essense (i.e.
ancestry_pathargument is implicit and not given, so we need to compute bottoms and use them instead). The new name is certainly better.
Yep, I agree, but all credit for this improvement goes to Jonathan; this was one of many good suggestions in his careful review.
Quoted 8 lines · Elijah Newren via GitGitGadget
Changes since v2:
* Incorporated Stolee's suggested patch as a preliminary cleanup of t6019. Slightly modified his patch by:
* fixing the "repetitive" typo pointed out by Eric
* removed the unnecessary "return 1" pointed out by Ævar
* switched "rev-list" to "log" since we are using --format anyway, in order to remove the need to call "sed" afterward
Thanks! I appreciate you fixing all of my mistakes and incorporating all of the existing feedback. It also looks nicer as your patch 2 than as a third on top.
This version looks good to me.
-Stolee
Quoted 10 lines · Derrick Stolee
Quoted 8 lines · Elijah Newren via GitGitGadget
Changes since v2:
* Incorporated Stolee's suggested patch as a preliminary cleanup of t6019. Slightly modified his patch by:
* fixing the "repetitive" typo pointed out by Eric
* removed the unnecessary "return 1" pointed out by Ævar
* switched "rev-list" to "log" since we are using --format anyway, in order to remove the need to call "sed" afterward
Quoted 5 lines
Thanks! I appreciate you fixing all of my mistakes and incorporating all of the existing feedback. It also looks nicer as your patch 2 than as a third on top.
This version looks good to me.
Yup, it looks good to me, too. Thanks, both.
Let's mark it for 'next' and merge it down soonish.
Quoted 13 lines · Junio C Hamano
Quoted 10 lines · Derrick Stolee
Quoted 8 lines · Elijah Newren via GitGitGadget
Changes since v2:
* Incorporated Stolee's suggested patch as a preliminary cleanup of t6019. Slightly modified his patch by:
* fixing the "repetitive" typo pointed out by Eric
* removed the unnecessary "return 1" pointed out by Ævar
* switched "rev-list" to "log" since we are using --format anyway, in order to remove the need to call "sed" afterward
Quoted 7 lines
Quoted 5 lines
Thanks! I appreciate you fixing all of my mistakes and incorporating all of the existing feedback. It also looks nicer as your patch 2 than as a third on top.
This version looks good to me.
Yup, it looks good to me, too. Thanks, both.
And thanks to all the other reviewers as well! Good suggestions came in from several different folks.
Quoted 1 line
Let's mark it for 'next' and merge it down soonish.
Sounds good.