Re: [RFC] Show empty root commits when using --simplify-by-decoration
- From
Pablo <pabloosabaterr@gmail.com>
- Date
- Apr 4, 2026, 16:08 UTC
- Message-ID
- <CAN5EUNTUDT7FMNYTOnTA_GJJXa2eZZL=RhJHfA6PfJvFSSHL-g@mail.gmail.com>
- In-Reply-To
- <adErl-EdiaXopVx-@x395.localdomain>
El sáb, 4 abr 2026 a las 17:18, Fernando Ramos (<greenfoo@u92.eu>) escribió:
Hi Fernando, I think I can bring some clarity to your second issue.
Show 47 quoted lines
>
> Regarding "--simplify-by-decoration", I found a somewhat unexpected behavior.
>
> Let's say we have a repository with two branches ("master" and "branch") and
> this history structure:
>
> a * <master> b * <branch>
> | |
> 2 *------------------'
> |
> 1 *
> |
> start *
>
> If I now run this...
>
> $ git log --graph --all --oneline --simplify-by-decoration
>
> ...I get this (which is expected and ok):
>
> * 0f3892d (branch) b
> | * 0c37d8c (master) a
> |/
> * d783631 Start
>
> HOWEVER, if the root commit ("start") is an empty commit, then the same command
> will print this instead:
>
> * 7abc10a (branch) b
> * 14d51e1 (master) a
>
> ...which is confusing for two reasons:
>
> 1. The graph is not showing a common origin.
> 2. Because "root" commits use an "*", it looks like "a" is the parent of "b",
> when the reality is that the graph is showing two independen origins.
>
> Issue (1) seems to be caused by the fact that empty root commits are marked as
> TREESAME and thus discarded when showing the graph. This can be fixed with
> patch #1 at the end of this email. Note, however, that this patch partially
> breaks what Taylor did on 1343c893138 (I say "partially" because it only happens
> when using "--simplify-by-decoration"). I have CC'ed Taylor in case he has any
> comments about it.
>
> Issue (2) can be "fixed" by using a diferent character (for example "I") for
> root commits (ie. those without parents), which can be done with patch #2 at the
> end of this email.This has actually been discussed before and it was ruled out some years ago. https://lore.kernel.org/git/xmqqwnwajbuj.fsf@gitster.c.googlers.com/
You would need more symbols for different roots, --boundary, --left-right and what if a commit that is not a root has its parents excluded, would you change the symbol even tho it is not a root?
You might find interesting a patch I'm working on that tackles that confusing look when two "parentless" commits are stacked. https://lore.kernel.org/git/20260404092425.550346-1-pabloosabaterr@gmail.com/
This answer from junio might be helpful as well: https://lore.kernel.org/git/xmqqbjfzn6ku.fsf@gitster.g/
Show 69 quoted lines
>
> With those two patches applied, this is what the output looks like in both cases
> (with the root commit being empty or not):
>
> * 7abc10a (branch) b
> | * 14d51e1 (master) a
> |/
> I c0db521 Start
>
> Let me know what you think and whether you want me to send a proper patch that
> includes (1) or (2) or (1)+(2).
>
> Thanks!
>
> PS: Path #3 at the end of this emails includes a dummy script to quickly
> recreate the two scenarios described above.
>
>
>
> --------------------------------------------------------------------------------
> Patch #1
> --------------------------------------------------------------------------------
>
> diff --git a/revision.c b/revision.c
> index c9b8bfd09f..0e7bf2ed3c 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -978,15 +978,17 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
> return;
>
> if (!commit->parents) {
> - /*
> - * Pretend as if we are comparing ourselves to the
> - * (non-existent) first parent of this commit object. Even
> - * though no such parent exists, its changed-path Bloom filter
> - * (if one exists) is relative to the empty tree, using Bloom
> - * filters is allowed here.
> - */
> - if (rev_same_tree_as_empty(revs, commit, 0))
> - commit->object.flags |= TREESAME;
> + if (!revs->simplify_by_decoration) {
> + /*
> + * Pretend as if we are comparing ourselves to the
> + * (non-existent) first parent of this commit object. Even
> + * though no such parent exists, its changed-path Bloom filter
> + * (if one exists) is relative to the empty tree, using Bloom
> + * filters is allowed here.
> + */
> + if (rev_same_tree_as_empty(revs, commit, 0))
> + commit->object.flags |= TREESAME;
> + }
> return;
> }
>
>
> --------------------------------------------------------------------------------
> Patch #2
> --------------------------------------------------------------------------------
>
> diff --git a/revision.c b/revision.c
> index 31808e3df0..c9b8bfd09f 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -4565,7 +4565,7 @@ const char *get_revision_mark(const struct rev_info *revs, const struct commit *
> else
> return ">";
> } else if (revs->graph)
> - return "*";
> + return commit->parents ? "*" : "I";This only focuses on real roots, what about commits with excluded parents. for example:
O---A
X---Y
If you'd run 'git log --graph O..A Y', it shows A, Y, X but
because it only look for roots it
ends up looking like: * A <- not a root but O is excluded so it seems like one
* Y
I XThis would make A and Y look related, also the "I" makes it very confusing because the vertical edges are very similar "|".
Show 78 quoted lines
> else if (revs->cherry_mark)
> return "+";
> return "";
>
>
> --------------------------------------------------------------------------------
> Patch #3
> --------------------------------------------------------------------------------
>
> diff --git a/build_example.sh b/build_example.sh
> new file mode 100755
> index 0000000000..60f63c5344
> --- /dev/null
> +++ b/build_example.sh
> @@ -0,0 +1,61 @@
> +#!/bin/bash
> +
> +################################################################################
> +# Example #1
> +################################################################################
> +#
> +# This will create this a repo with this history structure:
> +#
> +# a * (master) b * (branch)
> +# | |
> +# 2 * -----------------'
> +# |
> +# 1 *
> +# |
> +# start *
> +
> +rm -rf EXAMPLE1 || true; mkdir -p EXAMPLE1; cd EXAMPLE1
> +
> +touch start.txt; git init; git add start.txt; git commit -m 'Start'
> +touch 1.txt; git add 1.txt; git commit -m '1'
> +touch 2.txt; git add 2.txt; git commit -m '2'
> +touch a.txt; git add a.txt; git commit -m 'a'
> +git checkout -b branch HEAD^
> +touch b.txt; git add b.txt; git commit -m 'b'
> +
> +git log --graph --all --oneline --simplify-by-decoration
> +# The previous command will print this, which is what we expected:
> +#
> +# * 0f3892d (HEAD -> branch) b
> +# | * 0c37d8c (master) a
> +# |/
> +# * d783631 Start
> +
> +cd ..
> +
> +
> +
> +################################################################################
> +# Example #2
> +################################################################################
> +#
> +# This will create this a repo with the same structure as before, but this time
> +# the root commit ("start") is an empty one.
> +
> +rm -rf EXAMPLE2 || true; mkdir -p EXAMPLE2; cd EXAMPLE2
> +
> +git init; git commit --allow-empty -m 'Start'
> +touch 1.txt; git add 1.txt; git commit -m '1'
> +touch 2.txt; git add 2.txt; git commit -m '2'
> +touch a.txt; git add a.txt; git commit -m 'a'
> +git checkout -b branch HEAD^
> +touch b.txt; git add b.txt; git commit -m 'b'
> +
> +git log --graph --all --oneline --simplify-by-decoration
> +# The previous command will print this, which is unexpected:
> +#
> +# * 7abc10a (HEAD -> branch) b
> +# * 14d51e1 (master) a
> +
> +cd ..
> +
>
>One more thing, even if it's an RFC it would be much easier for reviewers if the patches were sent as git format-patch (one each email) with a cover letter, discussing your RFC, so anyone can use git am and b4 to apply the patches.
Hope this helps, Pablo