git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3] revision: --show-pulls adds helpful merges

From
Junio C Hamano <gitster@pobox.com>
Date
Apr 10, 2020, 20:06 UTC
Message-ID
<xmqq369bjebo.fsf@gitster.c.googlers.com>
In-Reply-To
<pull.599.v3.git.1586521183873.gitgitgadget@gmail.com>
"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
Show 21 quoted lines
>   A----C1----C2--- ... ---Cn----M------P1---P2--- ... ---Pn
>    \     \     \            \  /      /    /            /
>     \     \__.. \            \/ ..__T1    /           Tn
>      \           \__..       /\     ..__T2           /
>       \_____________________B  \____________________/
>
> If the commits T1, T2, ... Tn did not change the file, then all of
> P1 through Pn will be TREESAME to their first parent, but not
> TREESAME to their second. This means that all of those merge commits
> appear in the --full-history view, with edges that immediately
> collapse into the lower history without introducing interesting
> single-parent commits.
>
> The --simplify-merges option was introduced to remove these extra
> merge commits. By noticing that the rewritten parents are reachable
> from their first parents, those edges can be simplified away. Finally,
> the commits now look like single-parent commits that are TREESAME to
> their "only" parent. Thus, they are removed and this issue does not
> cause issues anymore. However, this also ends up removing the commit
> M from the history view! Even worse, the --simplify-merges option
> requires walking the entire history before returning a single result.

True. It is not advisable to use --simplify-merges unless you are limiting the history at the bottom for that reason.

Show 6 quoted lines
> In some sense, users are asking for the "first" merge commit to
> bring in the change to their branch. As long as the parent order is
> consistent, this can be handled with the following rule:
>
>   Include a merge commit if it is not TREESAME to its first
>   parent, but is TREESAME to a later parent.

"but is" -> "even if it is" would make it a bit more accurate, I would think. Normally, such a merge that is treesame to some side branch is omitted from the output, but the rule wants it to be shown even if all the changes were brought in from one single parent.

> Update Documentation/rev-list-options.txt with significant details
> around this option. This requires updating the example in the
> History Simplification section to demonstrate some of the problems
> with TREESAME second parents.
Good.
Show 18 quoted lines
> diff --git a/revision.c b/revision.c
> index 8136929e236..f89dd6caa55 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -870,7 +870,19 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
>  			}
>  			parent->next = NULL;
>  			commit->parents = parent;
> -			commit->object.flags |= TREESAME;
> +
> +			/*
> +			 * A merge commit is a "diversion" if it is not
> +			 * TREESAME to its first parent but is TREESAME
> +			 * to a later parent. In the simplified history,
> +			 * we "divert" the history walk to the later
> +			 * parent. These commits are shown when "show_pulls"
> +			 * is enabled, so do not mark the object as
> +			 * TREESAME here.

As we no longer use the word "diversion", this explanation should shift the focus from defining the word "diversion" and giving its background to explaining why the above parent rewriting is done and why the TREESAME marking is conditional, e.g.

      			The tree of the merge and of the parent are
      			the same; from here on, we stop following
      			histories of all other parents but this one
      			by culling commit->parents list.  We also
      			normally mark the merge commit TREESAME as
      			the merge itself did not introduce any
      			change relative to the parent, but we
      			refrain from doing so for the first parent
      			under "--show-pulls" mode, in order to let
      			the output phase to show the merge, which is
      			the last commit before we divert our walk to
      			a side history.
or something along that line.
> +			if (!revs->show_pulls || !nth_parent)
> +				commit->object.flags |= TREESAME;
> +
>  			return;
Show 7 quoted lines
> @@ -897,6 +909,10 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
>  				relevant_change = 1;
>  			else
>  				irrelevant_change = 1;
> +
> +			if (!nth_parent)
> +				commit->object.flags |= PULL_MERGE;

For a three-parent merge that brings in changes to the first parent, if the result matches the second parent, we would have returned in the previous hunk before having a chance to inspect the third one and marking the merge result with PULL_MERGE, but if you swap the order of the second and the third parent, the second parent, which has different tree from the result, would not return in the previous hunk and cause the merge with PULL_MERGE here. And then when we inspect the third parent, the previous hunk's return would kick in. So for two merges that merge exactly the same two branches on top of exactly the same commit on the mainline, you sometimes mark the result with PULL_MERGE and sometimes don't, depending on the order of the second and the third parent.

That feels iffy. Treating the first parent differently from others is what we intend to do with this change, bhut this hunk treats the other parents differently depending on the merge order.

Show 8 quoted lines
> @@ -3019,7 +3037,8 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
>  	if (!cnt ||
>  	    (commit->object.flags & UNINTERESTING) ||
>  	    !(commit->object.flags & TREESAME) ||
> -	    (parent = one_relevant_parent(revs, commit->parents)) == NULL)
> +	    (parent = one_relevant_parent(revs, commit->parents)) == NULL ||
> +	    (revs->show_pulls && (commit->object.flags & PULL_MERGE)))
>  		st->simplified = commit;
... hence, wouldn't we see different result here ...
Show 7 quoted lines
> @@ -3602,6 +3621,10 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
>  			/* drop merges unless we want parenthood */
>  			if (!want_ancestry(revs))
>  				return commit_ignore;
> +
> +			if (revs->show_pulls && (commit->object.flags & PULL_MERGE))
> +				return commit_show;
... and also here?
Thanks.
Previous: Derrick Stolee via GitGitGadgetNext: Derrick Stolee
Message 22 of 23 in “revision: --include-diversions adds helpful merges”
  1. revision: --include-diversions adds helpful mergesDerrick Stolee via GitGitGadget, Apr 8, 2020
  2. Junio C HamanoApr 8, 2020
  3. Derrick StoleeApr 8, 2020
  4. Derrick StoleeApr 8, 2020
  5. Junio C HamanoApr 8, 2020
  6. Jeff KingApr 8, 2020
  7. Derrick StoleeApr 8, 2020
  8. Junio C HamanoApr 8, 2020
  9. Derrick StoleeApr 8, 2020
  10. Junio C HamanoApr 9, 2020
  11. Derrick StoleeApr 9, 2020
  12. Philip OakleyApr 9, 2020
  13. Junio C HamanoApr 9, 2020
  14. Derrick StoleeApr 9, 2020
  15. Jeff KingApr 9, 2020
  16. Junio C HamanoApr 9, 2020
  17. Jeff KingApr 9, 2020
  18. brian m. carlsonApr 8, 2020
  19. Jeff KingApr 8, 2020
  20. revision: --include-diversions adds helpful mergesDerrick Stolee via GitGitGadget, Apr 9, 2020
  21. revision: --show-pulls adds helpful mergesDerrick Stolee via GitGitGadget, Apr 10, 2020
  22. Junio C HamanoApr 10, 2020
  23. Derrick StoleeApr 10, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.