odb/source-loose: wire up `count_objects()` callback

Move `odb_source_loose_count_objects()` and its associated helpers from "object-file.c" into "odb/source-loose.c" and wire it up as the `count_objects()` callback of the loose source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 1, 2026 at 10:20 UTC 2ade08ac2978dc1c908602c2a4d653836ecb5acb
5 files changed +65 -78
builtin/gc.c
+3 -3
@@ -466,6 +466,7 @@ out:
466
467 static int too_many_loose_objects(int limit)
468 {
469 + struct odb_source_files *files = odb_source_files_downcast(the_repository->objects->sources);
470 /*
471 * This is weird, but stems from legacy behaviour: the GC auto
472 * threshold was always essentially interpreted as if it was rounded up
@@ -474,9 +475,8 @@ static int too_many_loose_objects(int limit)
475 int auto_threshold = DIV_ROUND_UP(limit, 256) * 256;
476 unsigned long loose_count;
477
477 - if (odb_source_loose_count_objects(the_repository->objects->sources,
478 - ODB_COUNT_OBJECTS_APPROXIMATE,
479 - &loose_count) < 0)
478 + if (odb_source_count_objects(&files->loose->base, ODB_COUNT_OBJECTS_APPROXIMATE,
479 + &loose_count) < 0)
480 return 0;
481
482 return loose_count > auto_threshold;
object-file.c
-60
@@ -1602,66 +1602,6 @@ int for_each_loose_file_in_source(struct odb_source *source,
1602 return r;
1603 }
1604
1605 -static int count_loose_object(const struct object_id *oid UNUSED,
1606 - struct object_info *oi UNUSED,
1607 - void *payload)
1608 -{
1609 - unsigned long *count = payload;
1610 - (*count)++;
1611 - return 0;
1612 -}
1613 -
1614 -int odb_source_loose_count_objects(struct odb_source *source,
1615 - enum odb_count_objects_flags flags,
1616 - unsigned long *out)
1617 -{
1618 - struct odb_source_files *files = odb_source_files_downcast(source);
1619 - const unsigned hexsz = source->odb->repo->hash_algo->hexsz - 2;
1620 - char *path = NULL;
1621 - DIR *dir = NULL;
1622 - int ret;
1623 -
1624 - if (flags & ODB_COUNT_OBJECTS_APPROXIMATE) {
1625 - unsigned long count = 0;
1626 - struct dirent *ent;
1627 -
1628 - path = xstrfmt("%s/17", source->path);
1629 -
1630 - dir = opendir(path);
1631 - if (!dir) {
1632 - if (errno == ENOENT) {
1633 - *out = 0;
1634 - ret = 0;
1635 - goto out;
1636 - }
1637 -
1638 - ret = error_errno("cannot open object shard '%s'", path);
1639 - goto out;
1640 - }
1641 -
1642 - while ((ent = readdir(dir)) != NULL) {
1643 - if (strspn(ent->d_name, "0123456789abcdef") != hexsz ||
1644 - ent->d_name[hexsz] != '\0')
1645 - continue;
1646 - count++;
1647 - }
1648 -
1649 - *out = count * 256;
1650 - ret = 0;
1651 - } else {
1652 - struct odb_for_each_object_options opts = { 0 };
1653 - *out = 0;
1654 - ret = odb_source_for_each_object(&files->loose->base, NULL, count_loose_object,
1655 - out, &opts);
1656 - }
1657 -
1658 -out:
1659 - if (dir)
1660 - closedir(dir);
1661 - free(path);
1662 - return ret;
1663 -}
1664 -
1605 static int check_stream_oid(git_zstream *stream,
1606 const char *hdr,
1607 unsigned long size,
object-file.h
-14
@@ -96,20 +96,6 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
96 each_loose_subdir_fn subdir_cb,
97 void *data);
98
99 -/*
100 - * Count the number of loose objects in this source.
101 - *
102 - * The object count is approximated by opening a single sharding directory for
103 - * loose objects and scanning its contents. The result is then extrapolated by
104 - * 256. This should generally work as a reasonable estimate given that the
105 - * object hash is supposed to be indistinguishable from random.
106 - *
107 - * Returns 0 on success, a negative error code otherwise.
108 - */
109 -int odb_source_loose_count_objects(struct odb_source *source,
110 - enum odb_count_objects_flags flags,
111 - unsigned long *out);
112 -
99 /**
100 * format_object_header() is a thin wrapper around s xsnprintf() that
101 * writes the initial "<type> <obj-len>" part of the loose object
odb/source-files.c
+1 -1
@@ -109,7 +109,7 @@ static int odb_source_files_count_objects(struct odb_source *source,
109 if (!(flags & ODB_COUNT_OBJECTS_APPROXIMATE)) {
110 unsigned long loose_count;
111
112 - ret = odb_source_loose_count_objects(source, flags, &loose_count);
112 + ret = odb_source_count_objects(&files->loose->base, flags, &loose_count);
113 if (ret < 0)
114 goto out;
115
odb/source-loose.c
+61
@@ -520,6 +520,66 @@ static int odb_source_loose_find_abbrev_len(struct odb_source *source,
520 return ret;
521 }
522
523 +static int count_loose_object(const struct object_id *oid UNUSED,
524 + struct object_info *oi UNUSED,
525 + void *payload)
526 +{
527 + unsigned long *count = payload;
528 + (*count)++;
529 + return 0;
530 +}
531 +
532 +static int odb_source_loose_count_objects(struct odb_source *source,
533 + enum odb_count_objects_flags flags,
534 + unsigned long *out)
535 +{
536 + struct odb_source_loose *loose = odb_source_loose_downcast(source);
537 + const unsigned hexsz = source->odb->repo->hash_algo->hexsz - 2;
538 + char *path = NULL;
539 + DIR *dir = NULL;
540 + int ret;
541 +
542 + if (flags & ODB_COUNT_OBJECTS_APPROXIMATE) {
543 + unsigned long count = 0;
544 + struct dirent *ent;
545 +
546 + path = xstrfmt("%s/17", source->path);
547 +
548 + dir = opendir(path);
549 + if (!dir) {
550 + if (errno == ENOENT) {
551 + *out = 0;
552 + ret = 0;
553 + goto out;
554 + }
555 +
556 + ret = error_errno("cannot open object shard '%s'", path);
557 + goto out;
558 + }
559 +
560 + while ((ent = readdir(dir)) != NULL) {
561 + if (strspn(ent->d_name, "0123456789abcdef") != hexsz ||
562 + ent->d_name[hexsz] != '\0')
563 + continue;
564 + count++;
565 + }
566 +
567 + *out = count * 256;
568 + ret = 0;
569 + } else {
570 + struct odb_for_each_object_options opts = { 0 };
571 + *out = 0;
572 + ret = odb_source_for_each_object(&loose->base, NULL, count_loose_object,
573 + out, &opts);
574 + }
575 +
576 +out:
577 + if (dir)
578 + closedir(dir);
579 + free(path);
580 + return ret;
581 +}
582 +
583 static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
584 {
585 oidtree_clear(loose->cache);
@@ -577,6 +637,7 @@ struct odb_source_loose *odb_source_loose_new(struct odb_source_files *files)
637 loose->base.read_object_stream = odb_source_loose_read_object_stream;
638 loose->base.for_each_object = odb_source_loose_for_each_object;
639 loose->base.find_abbrev_len = odb_source_loose_find_abbrev_len;
640 + loose->base.count_objects = odb_source_loose_count_objects;
641
642 if (!is_absolute_path(loose->base.path))
643 chdir_notify_register(NULL, odb_source_loose_reparent, loose);