Git development
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: "Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, "SZEDER Gábor" <szeder.dev@gmail.com>,
	"Michael Montalbo" <mmontalbo@gmail.com>
Subject: Re: [PATCH] revision: make get_commit_action() a pure predicate
Date: Mon, 27 Jul 2026 06:45:41 -0700	[thread overview]
Message-ID: <xmqq8q6wpmuy.fsf@gitster.g> (raw)
In-Reply-To: <pull.2169.git.1784143793613.gitgitgadget@gmail.com> (Michael Montalbo via GitGitGadget's message of "Wed, 15 Jul 2026 19:29:52 +0000")

"Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com> writes:

> commit_early_ignore() runs twice on the -L path, once for that gate and
> once inside get_commit_action(), but it reads only object flags and pack
> membership, disjoint from the TREESAME flag the fold sets, so the repeat
> is harmless.

This one confused me a bit, so I'll think aloud below to see if you
can spot where I am misunderstanding your code.

> +/*
> + * Whether the commit is ignored by the cheap checks that read only its
> + * traversal flags and pack membership (e.g. already shown, or marked
> + * uninteresting), before any check that examines the commit's date,
> + * parents, message, or diff.
> + */
> +static int commit_early_ignore(struct rev_info *revs, struct commit *commit)
>  {
>  	if (commit->object.flags & SHOWN)
> -		return commit_ignore;
> +		return 1;
>  	if (revs->maximal_only && (commit->object.flags & CHILD_VISITED))
> -		return commit_ignore;
> +		return 1;
>  	if (revs->unpacked && has_object_pack(revs->repo, &commit->object.oid))
> -		return commit_ignore;
> -	if (revs->no_kept_objects) {
> -		if (has_object_kept_pack(revs->repo, &commit->object.oid,
> -					 revs->keep_pack_cache_flags))
> -			return commit_ignore;
> -	}
> +		return 1;
> +	if (revs->no_kept_objects &&
> +	    has_object_kept_pack(revs->repo, &commit->object.oid,
> +				 revs->keep_pack_cache_flags))
> +		return 1;
>  	if (commit->object.flags & UNINTERESTING)
> +		return 1;
> +	return 0;
> +}

This mirrors what the original get_commit_action() did to return
early with 'commit_ignore'.  Collapsing the nested 'if' for the
kept-objects case is a nice touch that makes the result easier to
follow.

> +/*
> + * Decide whether this commit is shown or ignored.  Keep it a pure
> + * predicate: callers such as the commit graph depend on it having no
> + * side effects, so per-commit mutations (such as -L range tracking)
> + * belong in the caller, simplify_commit(), not here.
> + */
> +enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
> +{
> +	if (commit_early_ignore(revs, commit))
>  		return commit_ignore;
> -	if (revs->line_level_traverse && !want_ancestry(revs)) {
> -		/*
> -		 * In case of line-level log with parent rewriting
> -		 * prepare_revision_walk() already took care of all line-level
> -		 * log filtering, and there is nothing left to do here.
> -		 *
> -		 * If parent rewriting was not requested, then this is the
> -		 * place to perform the line-level log filtering.  Notably,
> -		 * this check, though expensive, must come before the other,
> -		 * cheaper filtering conditions, because the tracked line
> -		 * ranges must be adjusted even when the commit will end up
> -		 * being ignored based on other conditions.
> -		 */
> -		if (!line_log_process_ranges_arbitrary_commit(revs, commit))
> -			return commit_ignore;
> -	}
>  	if (revs->min_age != -1 &&
>  	    comparison_date(revs, commit) > revs->min_age)
>  			return commit_ignore;
> @@ -4314,7 +4316,23 @@ struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit
>  
>  enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
>  {
> -	enum commit_action action = get_commit_action(revs, commit);
> +	enum commit_action action;
> +
> +	/*
> +	 * For a line-level log without parent rewriting, fold each commit's
> +	 * ranges as the walk reaches it (parent rewriting does this eagerly in
> +	 * prepare_revision_walk()).  Fold before get_commit_action() so the
> +	 * ranges carry across a commit that a later, cheaper check ignores;
> +	 * the commit_early_ignore() guard skips a commit get_commit_action()
> +	 * would ignore outright.
> +	 */
> +	if (revs->line_level_traverse && !want_ancestry(revs) &&
> +	    !commit_early_ignore(revs, commit)) {
> +		if (!line_log_process_ranges_arbitrary_commit(revs, commit))
> +			return commit_ignore;
> +	}
> +
> +	action = get_commit_action(revs, commit);

The primary change in the patch is to lift the "line-level" code out
of get_commit_action() and move it to one of its callers (namely
simplify_commit()).  The other caller is known not to trigger the
affected parts of the function, which was discussed previously at
https://lore.kernel.org/git/xmqqtsqxfdl4.fsf@gitster.g/ and started
this leftover bit.

We used to call get_commit_action() to decide the fate of the
commit.  If get_commit_action() returned anything other than
'commit_show', simplify_commit() simply returned that action without
doing anything further.

The original get_commit_action(), when on the code path that calls
line_log_process_ranges_arbitrary_commit() to check if we want to
ignore this commit, did what the commit_early_ignore() helper does
in this version before reaching that point.  So this updated caller
in simplify_commit() recreates the exact same logic.

We do end up executing the commit_early_ignore() logic twice if
line_log_process_ranges_arbitrary_commit() does not tell us to ignore
this commit.  With only two callers of get_commit_action(), we could
easily reuse the result of commit_early_ignore() if we wanted to, but
it is probably not worth it.

So the patch looks good.  Will queue.  Thanks.

      parent reply	other threads:[~2026-07-27 13:45 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-15 19:29 [PATCH] revision: make get_commit_action() a pure predicate Michael Montalbo via GitGitGadget
2026-07-24 21:37 ` Junio C Hamano
2026-07-25 19:25   ` Michael Montalbo
2026-07-27 13:25     ` Junio C Hamano
2026-07-27 13:45 ` Junio C Hamano [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqq8q6wpmuy.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    --cc=gitgitgadget@gmail.com \
    --cc=mmontalbo@gmail.com \
    --cc=szeder.dev@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox