odb: introduce generic object counting

Similar to the preceding commit, introduce counting of objects on the object database level, replacing the logic that we have in `repo_approximate_object_count()`. Note that the function knows to cache the object count. It's unclear whether this cache is really required as we shouldn't have that many cases where we count objects repeatedly. But to be on the safe side the caching mechanism is retained, with the only excepting being that we also have to use the passed flags as caching key. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Mar 12, 2026 at 09:43 UTC 6801ffd37df8420917e1feaf03b5f7c175798bff
7 files changed +64 -40
builtin/gc.c
+5 -1
@@ -574,9 +574,13 @@ static uint64_t total_ram(void)
574 static uint64_t estimate_repack_memory(struct gc_config *cfg,
575 struct packed_git *pack)
576 {
577 - unsigned long nr_objects = repo_approximate_object_count(the_repository);
577 + unsigned long nr_objects;
578 size_t os_cache, heap;
579
580 + if (odb_count_objects(the_repository->objects,
581 + ODB_COUNT_OBJECTS_APPROXIMATE, &nr_objects) < 0)
582 + return 0;
583 +
584 if (!pack || !nr_objects)
585 return 0;
586
commit-graph.c
+2 -1
@@ -2607,7 +2607,8 @@ int write_commit_graph(struct odb_source *source,
2607 replace = ctx.opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE;
2608 }
2609
2610 - ctx.approx_nr_objects = repo_approximate_object_count(r);
2610 + if (odb_count_objects(r->objects, ODB_COUNT_OBJECTS_APPROXIMATE, &ctx.approx_nr_objects) < 0)
2611 + ctx.approx_nr_objects = 0;
2612
2613 if (ctx.append && g) {
2614 for (i = 0; i < g->num_commits; i++) {
object-name.c
+5 -1
@@ -837,7 +837,11 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
837 const unsigned hexsz = algo->hexsz;
838
839 if (len < 0) {
840 - unsigned long count = repo_approximate_object_count(r);
840 + unsigned long count;
841 +
842 + if (odb_count_objects(r->objects, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
843 + count = 0;
844 +
845 /*
846 * Add one because the MSB only tells us the highest bit set,
847 * not including the value of all the _other_ bits (so "15"
odb.c
+36 -1
@@ -917,6 +917,41 @@ int odb_for_each_object(struct object_database *odb,
917 return 0;
918 }
919
920 +int odb_count_objects(struct object_database *odb,
921 + enum odb_count_objects_flags flags,
922 + unsigned long *out)
923 +{
924 + struct odb_source *source;
925 + unsigned long count = 0;
926 + int ret;
927 +
928 + if (odb->object_count_valid && odb->object_count_flags == flags) {
929 + *out = odb->object_count;
930 + return 0;
931 + }
932 +
933 + odb_prepare_alternates(odb);
934 + for (source = odb->sources; source; source = source->next) {
935 + unsigned long c;
936 +
937 + ret = odb_source_count_objects(source, flags, &c);
938 + if (ret < 0)
939 + goto out;
940 +
941 + count += c;
942 + }
943 +
944 + odb->object_count = count;
945 + odb->object_count_valid = 1;
946 + odb->object_count_flags = flags;
947 +
948 + *out = count;
949 + ret = 0;
950 +
951 +out:
952 + return ret;
953 +}
954 +
955 void odb_assert_oid_type(struct object_database *odb,
956 const struct object_id *oid, enum object_type expect)
957 {
@@ -1030,7 +1065,7 @@ void odb_reprepare(struct object_database *o)
1065 for (source = o->sources; source; source = source->next)
1066 odb_source_reprepare(source);
1067
1033 - o->approximate_object_count_valid = 0;
1068 + o->object_count_valid = 0;
1069
1070 obj_read_unlock();
1071 }
odb.h
+16 -3
@@ -110,10 +110,11 @@ struct object_database {
110 /*
111 * A fast, rough count of the number of objects in the repository.
112 * These two fields are not meant for direct access. Use
113 - * repo_approximate_object_count() instead.
113 + * odb_count_objects() instead.
114 */
115 - unsigned long approximate_object_count;
116 - unsigned approximate_object_count_valid : 1;
115 + unsigned long object_count;
116 + unsigned object_count_flags;
117 + unsigned object_count_valid : 1;
118
119 /*
120 * Submodule source paths that will be added as additional sources to
@@ -509,6 +510,18 @@ enum odb_count_objects_flags {
510 ODB_COUNT_OBJECTS_APPROXIMATE = (1 << 0),
511 };
512
513 +/*
514 + * Count the number of objects in the given object database. This object count
515 + * may double-count objects that are stored in multiple backends, or which are
516 + * stored multiple times in a single backend.
517 + *
518 + * Returns 0 on success, a negative error code otherwise. The number of objects
519 + * will be assigned to the `out` pointer on success.
520 + */
521 +int odb_count_objects(struct object_database *odb,
522 + enum odb_count_objects_flags flags,
523 + unsigned long *out);
524 +
525 enum {
526 /*
527 * By default, `odb_write_object()` does not actually write anything
packfile.c
-27
@@ -1132,33 +1132,6 @@ out:
1132 return ret;
1133 }
1134
1135 -/*
1136 - * Give a fast, rough count of the number of objects in the repository. This
1137 - * ignores loose objects completely. If you have a lot of them, then either
1138 - * you should repack because your performance will be awful, or they are
1139 - * all unreachable objects about to be pruned, in which case they're not really
1140 - * interesting as a measure of repo size in the first place.
1141 - */
1142 -unsigned long repo_approximate_object_count(struct repository *r)
1143 -{
1144 - if (!r->objects->approximate_object_count_valid) {
1145 - struct odb_source *source;
1146 - unsigned long count = 0;
1147 -
1148 - odb_prepare_alternates(r->objects);
1149 - for (source = r->objects->sources; source; source = source->next) {
1150 - unsigned long c;
1151 -
1152 - if (!odb_source_count_objects(source, ODB_COUNT_OBJECTS_APPROXIMATE, &c))
1153 - count += c;
1154 - }
1155 -
1156 - r->objects->approximate_object_count = count;
1157 - r->objects->approximate_object_count_valid = 1;
1158 - }
1159 - return r->objects->approximate_object_count;
1160 -}
1161 -
1135 unsigned long unpack_object_header_buffer(const unsigned char *buf,
1136 unsigned long len, enum object_type *type, unsigned long *sizep)
1137 {
packfile.h
-6
@@ -375,12 +375,6 @@ int packfile_store_for_each_object(struct packfile_store *store,
375 #define PACKDIR_FILE_GARBAGE 4
376 extern void (*report_garbage)(unsigned seen_bits, const char *path);
377
378 -/*
379 - * Give a rough count of objects in the repository. This sacrifices accuracy
380 - * for speed.
381 - */
382 -unsigned long repo_approximate_object_count(struct repository *r);
383 -
378 void pack_report(struct repository *repo);
379
380 /*