Subject: [PATCH v2 0/2] Allow --ancestry-path to take an argument

From: "Elijah Newren via GitGitGadget" <[email-masked]>
Date: Thu, 18 Aug 2022 06:17:45 +0000

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/[email-masked]/

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
       }

-- 
gitgitgadget