Skip to content

Commit

Permalink
survey: add object count summary
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
derrickstolee authored and dscho committed Dec 30, 2024
1 parent dcde0e1 commit ae8bf20
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 11 deletions.
6 changes: 6 additions & 0 deletions Documentation/git-survey.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
"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
---
Part of the linkgit:git[1] suite
131 changes: 124 additions & 7 deletions builtin/survey.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

#include "builtin.h"
#include "config.h"
#include "environment.h"
#include "hex.h"
#include "object.h"
#include "object-name.h"
#include "object-store-ll.h"
#include "parse-options.h"
#include "path-walk.h"
#include "progress.h"
#include "ref-filter.h"
#include "refs.h"
#include "revision.h"
#include "strbuf.h"
#include "strvec.h"
#include "tag.h"
#include "trace2.h"

static const char * const survey_usage[] = {
Expand Down Expand Up @@ -46,12 +53,20 @@ struct survey_report_ref_summary {
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
* at the end of the exploration of the repository and its references.
*/
struct survey_report {
struct survey_report_ref_summary refs;
struct survey_report_object_summary reachable_objects;
};

struct survey_context {
Expand All @@ -74,10 +89,12 @@ struct survey_context {
size_t progress_total;

struct strvec refs;
struct ref_array ref_array;
};

static void clear_survey_context(struct survey_context *ctx)
{
ref_array_clear(&ctx->ref_array);
strvec_clear(&ctx->refs);
}

Expand Down Expand Up @@ -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)
{
size_t width = 3 * (nr - 1);
size_t min_width = strlen(name);

for (size_t i = 0; i < nr; i++)
width += widths[i];

if (width < min_width)
width = min_width;

if (width > section_len)
width = section_len;

Expand Down Expand Up @@ -228,11 +249,43 @@ static void survey_report_plaintext_refs(struct survey_context *ctx)
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)
{
printf("GIT SURVEY for \"%s\"\n", ctx->repo->worktree);
printf("-----------------------------------------------------\n");
survey_report_plaintext_refs(ctx);
survey_report_plaintext_reachable_object_summary(ctx);
}

/*
Expand Down Expand Up @@ -380,15 +433,13 @@ static void do_load_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);
do_load_refs(ctx, &ref_array);
do_load_refs(ctx, &ctx->ref_array);

ctx->report.refs.refs_nr = ref_array.nr;
for (int i = 0; i < ref_array.nr; i++) {
ctx->report.refs.refs_nr = ctx->ref_array.nr;
for (int i = 0; i < ctx->ref_array.nr; i++) {
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) {
case FILTER_REFS_TAGS:
Expand Down Expand Up @@ -418,8 +469,72 @@ static void survey_phase_refs(struct survey_context *ctx)
}

trace2_region_leave("survey", "phase/refs", ctx->repo);
}

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;

ref_array_clear(&ref_array);
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)
Expand Down Expand Up @@ -472,6 +587,8 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor

survey_phase_refs(&ctx);

survey_phase_objects(&ctx);

survey_report_plaintext(&ctx);

clear_survey_context(&ctx);
Expand Down
23 changes: 19 additions & 4 deletions t/t8100-git-survey.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ test_expect_success 'git survey -h shows experimental warning' '
'

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)' '
git survey >out 2>err &&
git survey --all-refs >out 2>err &&
test_line_count = 0 err &&
tr , " " >expect <<-EOF &&
Expand All @@ -33,8 +39,17 @@ test_expect_success 'git survey (default)' '
-----------------+------
, Branches | 1
Remote refs | 0
Tags (all) | 0
Tags (annotated) | 0
Tags (all) | 2
Tags (annotated) | 2
REACHABLE OBJECT SUMMARY
========================
Object Type | Count
------------+------
Tags | 4
Commits | 10
Trees | 10
Blobs | 10
EOF
test_cmp expect out
Expand Down

0 comments on commit ae8bf20

Please sign in to comment.