git/list[1] front-page[2] threads[3] people[4] search[5] about
wed 2026-10-07 16:52 UTC

Re: [PATCH v2 1/1] repo: add filtering options to "repo structure"

From
Patrick Steinhardt <ps@pks.im>
Date
Oct 6, 2026, 05:51 UTC
Message-ID
<asSMX-K2qLsaXc3v@pks.im>
In-Reply-To
<20261005174045.1900391-2-markchucarroll@fastmail.com>
On Mon, Oct 05, 2026 at 01:40:44PM -0400, Mark C. Chu-Carroll wrote:
> Implement filtering for repo structure, imitating the mechanism
> used in "git log".

The message should give an explanation of what this change does, and what the motivation behind it is.

Show 10 quoted lines
> diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
> index ed7d80c690..5cbdf8e727 100644
> --- a/Documentation/git-repo.adoc
> +++ b/Documentation/git-repo.adoc
> @@ -10,7 +10,7 @@ SYNOPSIS
>  [synopsis]
>  git repo info [--format=(lines|nul) | -z] [--all | <key>...]
>  git repo info --keys [--format=(lines|nul) | -z]
> -git repo structure [--format=(table|lines|nul) | -z]
> +git repo structure [--format=(table|lines|nul) | -z] [<include|^exclude>...]
I think we should probably have this be `[<revs>...]`.
Show 8 quoted lines
> @@ -56,9 +56,10 @@ supported:
>  `nul`:::
>  	Similar to `lines`, but using a _NUL_ character after each value.
>  
> -`structure [--format=(table|lines|nul) | -z]`::
> -	Retrieve statistics about the current repository structure. The
> -	following kinds of information are reported:
> +`structure [--format=(table|lines|nul) | -z] [<include|^exclude>...]::
Same here.
Show 6 quoted lines
> @@ -66,6 +67,16 @@ supported:
>  * Total disk size of reachable objects by type
>  * Largest reachable objects in the repository by type
>  +
> +The set of objects counted can be filtered by specifying a
> +collection of query clauses to select which objects will be

s/query clauses/revisions/, which is a well-defined term. So with this change I think we can drop most of the remaining paragraph, except for the last sentence.

Show 23 quoted lines
> +counted. These parameters follow the same syntax as the parameters
> +to similar commands like `git log`. Semantically, these parameters
> +are treated as a collection of include and exclude specifiers. Th
> +set of objects counted will consist of all objects reachable from
> +an object included by one of the include specifiers via a path that
> +does not include an object in an exclude clause. If no includes
> +are specified, then the include set is all reachable objects. 
> ++
>  The output format can be chosen through the flag `--format`. Three formats are
>  supported:
>  +
> diff --git a/builtin/repo.c b/builtin/repo.c
> index 84e012f83f..b3aca71298 100644
> --- a/builtin/repo.c
> +++ b/builtin/repo.c
> @@ -946,12 +946,20 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
>  		OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
>  		OPT_END()
>  	};
> +	struct setup_revision_opt s_r_opt;
> +	memset(&s_r_opt, 0, sizeof(s_r_opt));
> +	s_r_opt.def = "HEAD";
> +	s_r_opt.revarg_opt = REVARG_COMMITTISH;
This can be:
    struct setup_revision_opt s_r_opt = {
        .def = "HEAD",
        .revarg_opt = REVARG_COMMITTISH,
    };
But I wonder whether we want to pass it at all:
  - `.def` specifies the default, but do we even want to have one when
    the user has passed arguments?
  - `.revarg_opt` makes us treat it like a committish by default, but a
    user may for example want to figure out the size of all objects
    reachable from a specific tree, only.

So maybe we shouldn't be setting this at all and just pass `NULL` to `setup_revisions()`?

Show 5 quoted lines
> -	argc = parse_options(argc, argv, prefix, options, repo_structure_usage, 0);
> -	if (argc)
> -		usage(_("too many arguments"));
> +	argc = parse_options(argc, argv, prefix, options, repo_structure_usage,
> +			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);

Makes sense. Here we keep argv0 because of `setup_revisions()`' weird calling convention. And we also ignore any unknown options so that we can pass them along, too.

Show 9 quoted lines
>  	repo_init_revisions(repo, &revs, prefix);
> +	if (argc > 1) {
> +		argc = setup_revisions(argc, argv, &revs, &s_r_opt);
> +		if (argc > 1)
> +			usage(_("too many arguments"));
> +	}
>  
>  	if (show_progress < 0)
>  		show_progress = isatty(2);

And then, if we have any additional parameters then we pass it on to `setup_revisions()`.

Show 91 quoted lines
> diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh
> index 02cc2b594a..eb2c595955 100755
> --- a/t/t1901-repo-structure.sh
> +++ b/t/t1901-repo-structure.sh
> @@ -144,6 +144,90 @@ test_expect_success SHA1 'repository with references and objects' '
>  	)
>  '
>  
> +test_expect_success SHA1 'repository with references and objects, filtered' '
> +	test_when_finished "rm -rf repo" &&
> +	git init repo &&
> +	(
> +		cd repo &&
> +		test_commit_bulk 1005 &&
> +		git tag -a foo -m bar &&
> +
> +		oid="$(git rev-parse HEAD)" &&
> +		git update-ref refs/remotes/origin/foo "$oid" &&
> +		git checkout -b grobble &&
> +		test_commit_bulk --ref=refs/heads/grobble 20 &&
> +		git checkout master &&
> + 		test_commit_bulk 20 &&
> +		# Also creates a commit, tree, and blob.
> +		git notes add -m foo &&
> +
> +		# git-rev-list(1) --disk-usage=human option printing the full
> +		# "byte/bytes" unit string instead of just "B".
> +		cat >expect <<-EOF &&
> +		| Repository structure      | Value      |
> +		| ------------------------- | ---------- |
> +		| * References              |            |
> +		|   * Count                 |      5     |
> +		|     * Branches            |      2     |
> +		|     * Tags                |      1     |
> +		|     * Remotes             |      1     |
> +		|     * Others              |      1     |
> +		|                           |            |
> +		| * Reachable objects       |            |
> +		|   * Count                 |   3.06 k   |
> +		|     * Commits             |   1.05 k   |
> +		|     * Trees               |   1.01 k   |
> +		|     * Blobs               |   1.01 k   |
> +		|     * Tags                |      1     |
> +		|   * Inflated size         |  16.04 MiB |
> +		|     * Commits             | 226.54 KiB |
> +		|     * Trees               |  15.81 MiB |
> +		|     * Blobs               |  11.68 KiB |
> +		|     * Tags                |    132 B   |
> +		|   * Disk size             | $(object_type_disk_usage all true) |
> +		|     * Commits             | $(object_type_disk_usage commit true) |
> +		|     * Trees               | $(object_type_disk_usage tree true) |
> +		|     * Blobs               |  $(object_type_disk_usage blob true) |
> +		|     * Tags                |    $(object_type_disk_usage tag) B   |
> +		|                           |            |
> +		| * Largest objects         |            |
> +		|   * Commits               |            |
> +		|     * Maximum size    [1] |    223 B   |
> +		|     * Maximum parents [2] |      1     |
> +		|   * Trees                 |            |
> +		|     * Maximum size    [3] |  32.29 KiB |
> +		|     * Maximum entries [4] |   1.01 k   |
> +		|   * Blobs                 |            |
> +		|     * Maximum size    [5] |     13 B   |
> +		|   * Tags                  |            |
> +		|     * Maximum size    [6] |    132 B   |
> +
> +		[1] 0dc91eb18580102a3a216c8bfecedeba2b9f9b9a
> +		[2] df6400c01440c329f1011669c4c26cc0c7852887
> +		[3] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c
> +		[4] 60665251ab71dbd8c18d9bf2174f4ee0d58aa06c
> +		[5] 97d808e45116bf02103490294d3d46dad7a2ac62
> +		[6] 4dae4f5954f5e6feb3577cfb1b181daa3fd3afd2
> +		EOF
> +
> +		git repo structure  >actual 2>actual-err &&
> +		cp actual /tmp/actual &&
> +		cp expect /tmp/expect &&
> +		test_cmp expect actual &&
> +		test_line_count = 0 actual-err &&
> +
> +		git repo structure grobble ^master >actual 2>actual-err &&
> +		cp actual /tmp &&
> +		cp actual-err /tmp &&
> +		test_grep "|     \* Commits             |    21     |" actual &&
> +		test_grep "|     \* Trees               |     2     |" actual &&
> +		test_grep "|     \* Commits             |  4.50 KiB |" actual &&
> +		test_grep "|     \* Trees               | 32.35 KiB |" actual &&
> +		test_grep "|     \* Blobs               | 11.68 KiB |" actual &&
> +		test_line_count = 0 actual-err
> +	)
> +'

I wonder whether we maybe want to have some additional tests that assert that you can also pass e.g.:

  - A tree or blob.
  - Revision options, like for example `--all --filter=object:type=blob`.

To make the test a bit less repetitive we might also want to use `--format=lines` and then only check for "objects.*.{inflated,disk}_size" to exercise only the parts that matter to this test.

Thanks!
Patrick
Previous: Mark C. Chu-Carroll
Message 7 of 7 in “repo: add filtering options to "repo structure"”
  1. 0/1 repo: add filtering options to "repo structure"Mark C. Chu-Carroll, Sep 24, 2026
  2. 1/1 repo: add filtering options to "repo structure"Mark C. Chu-Carroll, Sep 24, 2026
  3. Patrick SteinhardtSep 30, 2026
  4. 0/1 repo: add filtering options to "repo structure"Mark C. Chu-Carroll, Oct 5, 2026
  5. 1/1 repo: add filtering options to "repo structure"Mark C. Chu-Carroll, Oct 5, 2026
  6. Mark C. Chu-CarrollOct 5, 2026
  7. Patrick SteinhardtOct 6, 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.