From: Mark C. Chu-Carroll Date: Mon, 05 Oct 2026 17:40:44 GMT Subject: [PATCH v2 1/1] repo: add filtering options to "repo structure" Message-ID: <20261005174045.1900391-2-markchucarroll@fastmail.com> In-Reply-To: <20261005174045.1900391-1-markchucarroll@fastmail.com> Implement filtering for repo structure, imitating the mechanism used in "git log". Signed-off-by: Mark C. Chu-Carroll --- Documentation/git-repo.adoc | 41 ++++++++++++++++-- builtin/repo.c | 16 +++++-- t/t1901-repo-structure.sh | 84 +++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 8 deletions(-) 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 | ...] git repo info --keys [--format=(lines|nul) | -z] -git repo structure [--format=(table|lines|nul) | -z] +git repo structure [--format=(table|lines|nul) | -z] [...] DESCRIPTION ----------- @@ -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] [...]:: ++ +Retrieve statistics about the current repository structure. The +following kinds of information are reported: + * Reference counts categorized by type * Reachable object counts categorized by type @@ -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 +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: + @@ -141,6 +152,28 @@ using the `nul` format: git repo info --format=nul layout.bare layout.shallow ------------ +* Generates information about storage usage in the repository: ++ +------------ +git repo structure +------------ ++ + +* Generates information about storage usage in the repository omitting +the branch "foo": ++ +------------ +git repo structure ^foo +------------ ++ +* Generates information about repository objects reachable from +the references "x" and "y", but omitting anything that can +only be reached on a path including "xchild": ++ +------------ +git repo structure x y ^xchild +------------ ++ SEE ALSO -------- linkgit:git-rev-parse[1] diff --git a/builtin/repo.c b/builtin/repo.c index 84e012f83f..b3aca71298 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -27,7 +27,7 @@ "git repo info --keys [--format=(lines|nul) | -z]" #define REPO_STRUCTURE_USAGE \ - "git repo structure [--format=(table|lines|nul) | -z]" + "git repo structure [--format=(table|lines|nul) | -z] [...]" static const char *const repo_usage[] = { REPO_INFO_USAGE, @@ -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; - 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); 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); 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 + ) +' + test_expect_success SHA1 'lines and nul format' ' test_when_finished "rm -rf repo" && git init repo && -- 2.53.0