#include "builtin.h"
#include "environment.h"
#include "parse-options.h"
+#include "path-walk.h"
#include "quote.h"
#include "ref-filter.h"
#include "refs.h"
+#include "revision.h"
#include "strbuf.h"
#include "string-list.h"
#include "shallow.h"
size_t others;
};
+struct object_stats {
+ size_t tags;
+ size_t commits;
+ size_t trees;
+ size_t blobs;
+};
+
+struct repo_stats {
+ struct ref_stats refs;
+ struct object_stats objects;
+};
+
struct stats_table {
struct string_list rows;
va_end(ap);
}
-static void stats_table_setup(struct stats_table *table, struct ref_stats *refs)
+static inline size_t get_total_object_count(struct object_stats *stats)
{
+ return stats->tags + stats->commits + stats->trees + stats->blobs;
+}
+
+static void stats_table_setup(struct stats_table *table, struct repo_stats *stats)
+{
+ struct object_stats *objects = &stats->objects;
+ struct ref_stats *refs = &stats->refs;
+ size_t object_total;
size_t ref_total;
ref_total = refs->branches + refs->remotes + refs->tags + refs->others;
stats_table_count_addf(table, refs->tags, " * %s", _("Tags"));
stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes"));
stats_table_count_addf(table, refs->others, " * %s", _("Others"));
+
+ object_total = get_total_object_count(objects);
+ stats_table_addf(table, "");
+ stats_table_addf(table, "* %s", _("Reachable objects"));
+ stats_table_count_addf(table, object_total, " * %s", _("Count"));
+ stats_table_count_addf(table, objects->commits, " * %s", _("Commits"));
+ stats_table_count_addf(table, objects->trees, " * %s", _("Trees"));
+ stats_table_count_addf(table, objects->blobs, " * %s", _("Blobs"));
+ stats_table_count_addf(table, objects->tags, " * %s", _("Tags"));
}
static inline size_t max_size_t(size_t a, size_t b)
}
}
+static int count_objects(const char *path UNUSED, struct oid_array *oids,
+ enum object_type type, void *cb_data)
+{
+ struct object_stats *stats = cb_data;
+
+ switch (type) {
+ case OBJ_TAG:
+ stats->tags += oids->nr;
+ break;
+ case OBJ_COMMIT:
+ stats->commits += oids->nr;
+ break;
+ case OBJ_TREE:
+ stats->trees += oids->nr;
+ break;
+ case OBJ_BLOB:
+ stats->blobs += oids->nr;
+ break;
+ default:
+ BUG("invalid object type");
+ }
+
+ return 0;
+}
+
+static void stats_count_objects(struct object_stats *stats,
+ struct ref_array *refs, struct rev_info *revs)
+{
+ struct path_walk_info info = PATH_WALK_INFO_INIT;
+
+ info.revs = revs;
+ info.path_fn = count_objects;
+ info.path_fn_data = stats;
+
+ for (int i = 0; i < refs->nr; i++) {
+ struct ref_array_item *ref = refs->items[i];
+
+ switch (ref->kind) {
+ case FILTER_REFS_BRANCHES:
+ case FILTER_REFS_TAGS:
+ case FILTER_REFS_REMOTES:
+ case FILTER_REFS_OTHERS:
+ add_pending_oid(revs, NULL, &ref->objectname, 0);
+ break;
+ default:
+ BUG("unexpected reference type");
+ }
+ }
+
+ walk_objects_by_path(&info);
+ path_walk_info_clear(&info);
+}
+
static int cmd_repo_stats(int argc, const char **argv, const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct ref_filter filter = REF_FILTER_INIT;
struct stats_table table = {
.rows = STRING_LIST_INIT_DUP,
};
- struct ref_stats stats = { 0 };
+ struct repo_stats stats = { 0 };
struct ref_array refs = { 0 };
+ struct rev_info revs;
struct option options[] = { 0 };
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
if (argc)
usage(_("too many arguments"));
+ repo_init_revisions(repo, &revs, prefix);
if (filter_refs(&refs, &filter, FILTER_REFS_REGULAR))
die(_("unable to filter refs"));
- stats_count_references(&stats, &refs);
+ stats_count_references(&stats.refs, &refs);
+ stats_count_objects(&stats.objects, &refs, &revs);
stats_table_setup(&table, &stats);
stats_table_print(&table);
stats_table_clear(&table);
+ release_revisions(&revs);
ref_array_clear(&refs);
return 0;
(
cd repo &&
cat >expect <<-\EOF &&
- | Repository stats | Value |
- | ---------------- | ----- |
- | * References | |
- | * Count | 0 |
- | * Branches | 0 |
- | * Tags | 0 |
- | * Remotes | 0 |
- | * Others | 0 |
+ | Repository stats | Value |
+ | ------------------- | ----- |
+ | * References | |
+ | * Count | 0 |
+ | * Branches | 0 |
+ | * Tags | 0 |
+ | * Remotes | 0 |
+ | * Others | 0 |
+ | | |
+ | * Reachable objects | |
+ | * Count | 0 |
+ | * Commits | 0 |
+ | * Trees | 0 |
+ | * Blobs | 0 |
+ | * Tags | 0 |
EOF
git repo stats >out 2>err &&
)
'
-test_expect_success 'repository with references' '
+test_expect_success 'repository with references and objects' '
test_when_finished "rm -rf repo" &&
git init repo &&
(
cd repo &&
- git commit --allow-empty -m init &&
+ test_commit_bulk 42 &&
git tag -a foo -m bar &&
oid="$(git rev-parse HEAD)" &&
git update-ref refs/remotes/origin/foo "$oid" &&
+ # Also creates a commit, tree, and blob.
git notes add -m foo &&
cat >expect <<-\EOF &&
- | Repository stats | Value |
- | ---------------- | ----- |
- | * References | |
- | * Count | 4 |
- | * Branches | 1 |
- | * Tags | 1 |
- | * Remotes | 1 |
- | * Others | 1 |
+ | Repository stats | Value |
+ | ------------------- | ----- |
+ | * References | |
+ | * Count | 4 |
+ | * Branches | 1 |
+ | * Tags | 1 |
+ | * Remotes | 1 |
+ | * Others | 1 |
+ | | |
+ | * Reachable objects | |
+ | * Count | 130 |
+ | * Commits | 43 |
+ | * Trees | 43 |
+ | * Blobs | 43 |
+ | * Tags | 1 |
EOF
git repo stats >out 2>err &&