object-file: refactor freshening of objects

When writing an object that already exists in our object database we skip the write and instead only update mtimes of the object, either in its packed or loose object format. This logic is wholly contained in "object-file.c", but that file is really only concerned with loose objects. So it does not really make sense that it also contains the logic to freshen a packed object. Introduce a new `odb_freshen_object()` function that sits on the object database level and two functions `packfile_store_freshen_object()` and `odb_source_loose_freshen_object()`. Like this, the format-specific functions can be part of their respective subsystems, while the backend agnostic function to freshen an object sits at the object database layer. Note that this change also moves the logic that iterates through object sources from the object source layer into the object database layer. This change is intentional: object sources should ideally only have to worry about themselves, and coordination of different sources should be handled on the object database level. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Nov 3, 2025 at 08:42 UTC f2bd88a308a2754e727cb462e03102307cdfe004
6 files changed +46 -28
object-file.c
+5 -28
@@ -968,30 +968,10 @@ static int write_loose_object(struct odb_source *source,
968 FOF_SKIP_COLLISION_CHECK);
969 }
970
971 -static int freshen_loose_object(struct object_database *odb,
972 - const struct object_id *oid)
971 +int odb_source_loose_freshen_object(struct odb_source *source,
972 + const struct object_id *oid)
973 {
974 - odb_prepare_alternates(odb);
975 - for (struct odb_source *source = odb->sources; source; source = source->next)
976 - if (check_and_freshen_source(source, oid, 1))
977 - return 1;
978 - return 0;
979 -}
980 -
981 -static int freshen_packed_object(struct object_database *odb,
982 - const struct object_id *oid)
983 -{
984 - struct pack_entry e;
985 - if (!find_pack_entry(odb->repo, oid, &e))
986 - return 0;
987 - if (e.p->is_cruft)
988 - return 0;
989 - if (e.p->freshened)
990 - return 1;
991 - if (!freshen_file(e.p->pack_name))
992 - return 0;
993 - e.p->freshened = 1;
994 - return 1;
974 + return !!check_and_freshen_source(source, oid, 1);
975 }
976
977 int stream_loose_object(struct odb_source *source,
@@ -1073,12 +1053,10 @@ int stream_loose_object(struct odb_source *source,
1053 die(_("deflateEnd on stream object failed (%d)"), ret);
1054 close_loose_object(source, fd, tmp_file.buf);
1055
1076 - if (freshen_packed_object(source->odb, oid) ||
1077 - freshen_loose_object(source->odb, oid)) {
1056 + if (odb_freshen_object(source->odb, oid)) {
1057 unlink_or_warn(tmp_file.buf);
1058 goto cleanup;
1059 }
1081 -
1060 odb_loose_path(source, &filename, oid);
1061
1062 /* We finally know the object path, and create the missing dir. */
@@ -1137,8 +1115,7 @@ int write_object_file(struct odb_source *source,
1115 * it out into .git/objects/??/?{38} file.
1116 */
1117 write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
1140 - if (freshen_packed_object(source->odb, oid) ||
1141 - freshen_loose_object(source->odb, oid))
1118 + if (odb_freshen_object(source->odb, oid))
1119 return 0;
1120 if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
1121 return -1;
object-file.h
+3
@@ -59,6 +59,9 @@ void *odb_source_loose_map_object(struct odb_source *source,
59 int odb_source_loose_has_object(struct odb_source *source,
60 const struct object_id *oid);
61
62 +int odb_source_loose_freshen_object(struct odb_source *source,
63 + const struct object_id *oid);
64 +
65 /*
66 * Populate and return the loose object cache array corresponding to the
67 * given object ID.
odb.c
+16
@@ -987,6 +987,22 @@ int odb_has_object(struct object_database *odb, const struct object_id *oid,
987 return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0;
988 }
989
990 +int odb_freshen_object(struct object_database *odb,
991 + const struct object_id *oid)
992 +{
993 + struct odb_source *source;
994 +
995 + if (packfile_store_freshen_object(odb->packfiles, oid))
996 + return 1;
997 +
998 + odb_prepare_alternates(odb);
999 + for (source = odb->sources; source; source = source->next)
1000 + if (odb_source_loose_freshen_object(source, oid))
1001 + return 1;
1002 +
1003 + return 0;
1004 +}
1005 +
1006 void odb_assert_oid_type(struct object_database *odb,
1007 const struct object_id *oid, enum object_type expect)
1008 {
odb.h
+3
@@ -396,6 +396,9 @@ int odb_has_object(struct object_database *odb,
396 const struct object_id *oid,
397 unsigned flags);
398
399 +int odb_freshen_object(struct object_database *odb,
400 + const struct object_id *oid);
401 +
402 void odb_assert_oid_type(struct object_database *odb,
403 const struct object_id *oid, enum object_type expect);
404
packfile.c
+16
@@ -819,6 +819,22 @@ struct packed_git *packfile_store_load_pack(struct packfile_store *store,
819 return p;
820 }
821
822 +int packfile_store_freshen_object(struct packfile_store *store,
823 + const struct object_id *oid)
824 +{
825 + struct pack_entry e;
826 + if (!find_pack_entry(store->odb->repo, oid, &e))
827 + return 0;
828 + if (e.p->is_cruft)
829 + return 0;
830 + if (e.p->freshened)
831 + return 1;
832 + if (utime(e.p->pack_name, NULL))
833 + return 0;
834 + e.p->freshened = 1;
835 + return 1;
836 +}
837 +
838 void (*report_garbage)(unsigned seen_bits, const char *path);
839
840 static void report_helper(const struct string_list *list,
packfile.h
+3
@@ -163,6 +163,9 @@ struct list_head *packfile_store_get_packs_mru(struct packfile_store *store);
163 struct packed_git *packfile_store_load_pack(struct packfile_store *store,
164 const char *idx_path, int local);
165
166 +int packfile_store_freshen_object(struct packfile_store *store,
167 + const struct object_id *oid);
168 +
169 struct pack_window {
170 struct pack_window *next;
171 unsigned char *base;