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

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 X

This 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

Previous: Fernando RamosNext: Fernando Ramos
Message 2 of 3 in “[RFC] Show empty root commits when using --simplify-by-decoration”
  1. Fernando RamosApr 4, 2026
  2. PabloApr 4, 2026
  3. Fernando RamosApr 4, 2026

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.