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
/*