survey: add object count summary

At the moment, nothing is obvious about the reason for the use of the
path-walk API, but this will become more prevelant in future iterations. For
now, use the path-walk API to sum up the counts of each kind of object.

For example, this is the reachable object summary output for my local repo:

REACHABLE OBJECT SUMMARY
========================
Object Type |  Count
------------+-------
       Tags |   1343
    Commits | 179344
      Trees | 314350
      Blobs | 184030

Signed-off-by: Derrick Stolee <stolee@gmail.com>
This commit is contained in:
Derrick Stolee
2024-09-01 20:33:47 -04:00
committed by Johannes Schindelin
parent 0d8393e8c4
commit db19259aeb
3 changed files with 149 additions and 11 deletions

View File

@@ -72,6 +72,12 @@ The references summary includes a count of each kind of reference,
including branches, remote refs, and tags (split by "all" and including branches, remote refs, and tags (split by "all" and
"annotated"). "annotated").
Reachable Object Summary
~~~~~~~~~~~~~~~~~~~~~~~~
The reachable object summary shows the total number of each kind of Git
object, including tags, commits, trees, and blobs.
GIT GIT
--- ---
Part of the linkgit:git[1] suite Part of the linkgit:git[1] suite

View File

@@ -2,13 +2,20 @@
#include "builtin.h" #include "builtin.h"
#include "config.h" #include "config.h"
#include "environment.h"
#include "hex.h"
#include "object.h" #include "object.h"
#include "object-name.h"
#include "object-store-ll.h" #include "object-store-ll.h"
#include "parse-options.h" #include "parse-options.h"
#include "path-walk.h"
#include "progress.h" #include "progress.h"
#include "ref-filter.h" #include "ref-filter.h"
#include "refs.h"
#include "revision.h"
#include "strbuf.h" #include "strbuf.h"
#include "strvec.h" #include "strvec.h"
#include "tag.h"
#include "trace2.h" #include "trace2.h"
static const char * const survey_usage[] = { static const char * const survey_usage[] = {
@@ -46,12 +53,20 @@ struct survey_report_ref_summary {
size_t unknown_nr; size_t unknown_nr;
}; };
struct survey_report_object_summary {
size_t commits_nr;
size_t tags_nr;
size_t trees_nr;
size_t blobs_nr;
};
/** /**
* This struct contains all of the information that needs to be printed * This struct contains all of the information that needs to be printed
* at the end of the exploration of the repository and its references. * at the end of the exploration of the repository and its references.
*/ */
struct survey_report { struct survey_report {
struct survey_report_ref_summary refs; struct survey_report_ref_summary refs;
struct survey_report_object_summary reachable_objects;
}; };
struct survey_context { struct survey_context {
@@ -74,10 +89,12 @@ struct survey_context {
size_t progress_total; size_t progress_total;
struct strvec refs; struct strvec refs;
struct ref_array ref_array;
}; };
static void clear_survey_context(struct survey_context *ctx) static void clear_survey_context(struct survey_context *ctx)
{ {
ref_array_clear(&ctx->ref_array);
strvec_clear(&ctx->refs); strvec_clear(&ctx->refs);
} }
@@ -128,10 +145,14 @@ static const size_t section_len = 4 * SECTION_SEGMENT_LEN;
static void print_table_title(const char *name, size_t *widths, size_t nr) static void print_table_title(const char *name, size_t *widths, size_t nr)
{ {
size_t width = 3 * (nr - 1); size_t width = 3 * (nr - 1);
size_t min_width = strlen(name);
for (size_t i = 0; i < nr; i++) for (size_t i = 0; i < nr; i++)
width += widths[i]; width += widths[i];
if (width < min_width)
width = min_width;
if (width > section_len) if (width > section_len)
width = section_len; width = section_len;
@@ -228,11 +249,43 @@ static void survey_report_plaintext_refs(struct survey_context *ctx)
clear_table(&table); clear_table(&table);
} }
static void survey_report_plaintext_reachable_object_summary(struct survey_context *ctx)
{
struct survey_report_object_summary *objs = &ctx->report.reachable_objects;
struct survey_table table = SURVEY_TABLE_INIT;
char *fmt;
table.table_name = _("REACHABLE OBJECT SUMMARY");
strvec_push(&table.header, _("Object Type"));
strvec_push(&table.header, _("Count"));
fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)objs->tags_nr);
insert_table_rowv(&table, _("Tags"), fmt, NULL);
free(fmt);
fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)objs->commits_nr);
insert_table_rowv(&table, _("Commits"), fmt, NULL);
free(fmt);
fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)objs->trees_nr);
insert_table_rowv(&table, _("Trees"), fmt, NULL);
free(fmt);
fmt = xstrfmt("%"PRIuMAX"", (uintmax_t)objs->blobs_nr);
insert_table_rowv(&table, _("Blobs"), fmt, NULL);
free(fmt);
print_table_plaintext(&table);
clear_table(&table);
}
static void survey_report_plaintext(struct survey_context *ctx) static void survey_report_plaintext(struct survey_context *ctx)
{ {
printf("GIT SURVEY for \"%s\"\n", ctx->repo->worktree); printf("GIT SURVEY for \"%s\"\n", ctx->repo->worktree);
printf("-----------------------------------------------------\n"); printf("-----------------------------------------------------\n");
survey_report_plaintext_refs(ctx); survey_report_plaintext_refs(ctx);
survey_report_plaintext_reachable_object_summary(ctx);
} }
/* /*
@@ -380,15 +433,13 @@ static void do_load_refs(struct survey_context *ctx,
*/ */
static void survey_phase_refs(struct survey_context *ctx) static void survey_phase_refs(struct survey_context *ctx)
{ {
struct ref_array ref_array = { 0 };
trace2_region_enter("survey", "phase/refs", ctx->repo); trace2_region_enter("survey", "phase/refs", ctx->repo);
do_load_refs(ctx, &ref_array); do_load_refs(ctx, &ctx->ref_array);
ctx->report.refs.refs_nr = ref_array.nr; ctx->report.refs.refs_nr = ctx->ref_array.nr;
for (int i = 0; i < ref_array.nr; i++) { for (int i = 0; i < ctx->ref_array.nr; i++) {
unsigned long size; unsigned long size;
struct ref_array_item *item = ref_array.items[i]; struct ref_array_item *item = ctx->ref_array.items[i];
switch (item->kind) { switch (item->kind) {
case FILTER_REFS_TAGS: case FILTER_REFS_TAGS:
@@ -418,8 +469,72 @@ static void survey_phase_refs(struct survey_context *ctx)
} }
trace2_region_leave("survey", "phase/refs", ctx->repo); trace2_region_leave("survey", "phase/refs", ctx->repo);
}
ref_array_clear(&ref_array); static void increment_object_counts(
struct survey_report_object_summary *summary,
enum object_type type,
size_t nr)
{
switch (type) {
case OBJ_COMMIT:
summary->commits_nr += nr;
break;
case OBJ_TREE:
summary->trees_nr += nr;
break;
case OBJ_BLOB:
summary->blobs_nr += nr;
break;
case OBJ_TAG:
summary->tags_nr += nr;
break;
default:
break;
}
}
static int survey_objects_path_walk_fn(const char *path,
struct oid_array *oids,
enum object_type type,
void *data)
{
struct survey_context *ctx = data;
increment_object_counts(&ctx->report.reachable_objects,
type, oids->nr);
return 0;
}
static void survey_phase_objects(struct survey_context *ctx)
{
struct rev_info revs = REV_INFO_INIT;
struct path_walk_info info = PATH_WALK_INFO_INIT;
unsigned int add_flags = 0;
trace2_region_enter("survey", "phase/objects", ctx->repo);
info.revs = &revs;
info.path_fn = survey_objects_path_walk_fn;
info.path_fn_data = ctx;
repo_init_revisions(ctx->repo, &revs, "");
revs.tag_objects = 1;
for (int i = 0; i < ctx->ref_array.nr; i++) {
struct ref_array_item *item = ctx->ref_array.items[i];
add_pending_oid(&revs, NULL, &item->objectname, add_flags);
}
walk_objects_by_path(&info);
release_revisions(&revs);
trace2_region_leave("survey", "phase/objects", ctx->repo);
} }
int cmd_survey(int argc, const char **argv, const char *prefix, struct repository *repo) int cmd_survey(int argc, const char **argv, const char *prefix, struct repository *repo)
@@ -472,6 +587,8 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
survey_phase_refs(&ctx); survey_phase_refs(&ctx);
survey_phase_objects(&ctx);
survey_report_plaintext(&ctx); survey_report_plaintext(&ctx);
clear_survey_context(&ctx); clear_survey_context(&ctx);

View File

@@ -16,11 +16,17 @@ test_expect_success 'git survey -h shows experimental warning' '
' '
test_expect_success 'create a semi-interesting repo' ' test_expect_success 'create a semi-interesting repo' '
test_commit_bulk 10 test_commit_bulk 10 &&
git tag -a -m one one HEAD~5 &&
git tag -a -m two two HEAD~3 &&
git tag -a -m three three two &&
git tag -a -m four four three &&
git update-ref -d refs/tags/three &&
git update-ref -d refs/tags/two
' '
test_expect_success 'git survey (default)' ' test_expect_success 'git survey (default)' '
git survey >out 2>err && git survey --all-refs >out 2>err &&
test_line_count = 0 err && test_line_count = 0 err &&
tr , " " >expect <<-EOF && tr , " " >expect <<-EOF &&
@@ -33,8 +39,17 @@ test_expect_success 'git survey (default)' '
-----------------+------ -----------------+------
, Branches | 1 , Branches | 1
Remote refs | 0 Remote refs | 0
Tags (all) | 0 Tags (all) | 2
Tags (annotated) | 0 Tags (annotated) | 2
REACHABLE OBJECT SUMMARY
========================
Object Type | Count
------------+------
Tags | 4
Commits | 10
Trees | 10
Blobs | 10
EOF EOF
test_cmp expect out test_cmp expect out