odb: compute compat object ID in `odb_write_object_ext()`

Repositories can have a compatibility hash configured, which means that such a repository is expected to maintain a mapping between canonical and compatibility object hashes. Maintaining this mapping is the responsibility of the object database sources, where we either store them as part of the loose objects map or in packfile indices v3 (once we gain support for this feature). But besides storing these compatibility hashes, the sources are also responsible for generating the compatibility hash in the first place. This is somewhat unnecessary though, as the compatibility hash should be computed the same no matter which source is being used. The consequence is that we need to duplicate this functionality across the different backends, which does not make a lot of sense. Refactor the code so that we instead compute the compatibility hash in `odb_write_object_ext()` and then pass the computed value to the sources. No callers need adjustment as there are none that write objects via the source interfaces directly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jul 17, 2026 at 11:32 UTC 215d305f450ff0691d15daaa6ef72f77e8441b39
7 files changed +38 -32
odb.c
+24 -2
@@ -989,11 +989,33 @@ int odb_write_object_ext(struct object_database *odb,
989 const void *buf, unsigned long len,
990 enum object_type type,
991 struct object_id *oid,
992 - struct object_id *compat_oid,
992 + const struct object_id *compat_oid_in,
993 enum odb_write_object_flags flags)
994 {
995 + const struct git_hash_algo *compat = odb->repo->compat_hash_algo;
996 + struct object_id compat_oid, *compat_oid_p = NULL;
997 +
998 + if (compat) {
999 + const struct git_hash_algo *algo = odb->repo->hash_algo;
1000 +
1001 + if (compat_oid_in) {
1002 + oidcpy(&compat_oid, compat_oid_in);
1003 + } else if (type == OBJ_BLOB) {
1004 + hash_object_file(compat, buf, len, type, &compat_oid);
1005 + } else {
1006 + struct strbuf converted = STRBUF_INIT;
1007 + convert_object_file(odb->repo, &converted, algo, compat,
1008 + buf, len, type, 0);
1009 + hash_object_file(compat, converted.buf, converted.len,
1010 + type, &compat_oid);
1011 + strbuf_release(&converted);
1012 + }
1013 +
1014 + compat_oid_p = &compat_oid;
1015 + }
1016 +
1017 return odb_source_write_object(odb->sources, buf, len, type,
996 - oid, compat_oid, flags);
1018 + oid, compat_oid_p, flags);
1019 }
1020
1021 int odb_write_object_stream(struct object_database *odb,
odb.h
+6 -4
@@ -585,9 +585,11 @@ enum odb_write_object_flags {
585
586 /*
587 * Write an object into the object database. The object is being written into
588 - * the local alternate of the repository. If provided, the converted object ID
589 - * as well as the compatibility object ID are written to the respective
590 - * pointers.
588 + * the local alternate of the repository. If provided, the object ID of the
589 + * final object is written into `oid`.
590 + *
591 + * If the caller provides a `compat_oid`, then this compatibility object hash
592 + * will be stored instead of computing the compatibility hash ad-hoc.
593 *
594 * Returns 0 on success, a negative error code otherwise.
595 */
@@ -595,7 +597,7 @@ int odb_write_object_ext(struct object_database *odb,
597 const void *buf, unsigned long len,
598 enum object_type type,
599 struct object_id *oid,
598 - struct object_id *compat_oid,
600 + const struct object_id *compat_oid,
601 enum odb_write_object_flags flags);
602
603 static inline int odb_write_object(struct object_database *odb,
odb/source-files.c
+1 -1
@@ -163,7 +163,7 @@ static int odb_source_files_write_object(struct odb_source *source,
163 const void *buf, size_t len,
164 enum object_type type,
165 struct object_id *oid,
166 - struct object_id *compat_oid,
166 + const struct object_id *compat_oid,
167 enum odb_write_object_flags flags)
168 {
169 struct odb_source_files *files = odb_source_files_downcast(source);
odb/source-inmemory.c
+1 -1
@@ -231,7 +231,7 @@ static int odb_source_inmemory_write_object(struct odb_source *source,
231 const void *buf, size_t len,
232 enum object_type type,
233 struct object_id *oid,
234 - struct object_id *compat_oid UNUSED,
234 + const struct object_id *compat_oid UNUSED,
235 enum odb_write_object_flags flags UNUSED)
236 {
237 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
odb/source-loose.c
+3 -21
@@ -585,32 +585,14 @@ static int odb_source_loose_freshen_object(struct odb_source *source,
585 static int odb_source_loose_write_object(struct odb_source *source,
586 const void *buf, size_t len,
587 enum object_type type, struct object_id *oid,
588 - struct object_id *compat_oid_in,
588 + const struct object_id *compat_oid,
589 enum odb_write_object_flags flags)
590 {
591 struct odb_source_loose *loose = odb_source_loose_downcast(source);
592 const struct git_hash_algo *algo = source->odb->repo->hash_algo;
593 - const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
594 - struct object_id compat_oid;
593 char hdr[MAX_HEADER_LEN];
594 size_t hdrlen = sizeof(hdr);
595
598 - /* Generate compat_oid */
599 - if (compat) {
600 - if (compat_oid_in)
601 - oidcpy(&compat_oid, compat_oid_in);
602 - else if (type == OBJ_BLOB)
603 - hash_object_file(compat, buf, len, type, &compat_oid);
604 - else {
605 - struct strbuf converted = STRBUF_INIT;
606 - convert_object_file(source->odb->repo, &converted, algo, compat,
607 - buf, len, type, 0);
608 - hash_object_file(compat, converted.buf, converted.len,
609 - type, &compat_oid);
610 - strbuf_release(&converted);
611 - }
612 - }
613 -
596 /* Normally if we have it in the pack then we do not bother writing
597 * it out into .git/objects/??/?{38} file.
598 */
@@ -619,8 +601,8 @@ static int odb_source_loose_write_object(struct odb_source *source,
601 return 0;
602 if (write_loose_object(loose, oid, hdr, hdrlen, buf, len, 0, flags))
603 return -1;
622 - if (compat)
623 - return repo_add_loose_object_map(loose, oid, &compat_oid);
604 + if (compat_oid)
605 + return repo_add_loose_object_map(loose, oid, compat_oid);
606 return 0;
607 }
608
odb/source-packed.c
+1 -1
@@ -530,7 +530,7 @@ static int odb_source_packed_write_object(struct odb_source *source UNUSED,
530 size_t len UNUSED,
531 enum object_type type UNUSED,
532 struct object_id *oid UNUSED,
533 - struct object_id *compat_oid UNUSED,
533 + const struct object_id *compat_oid UNUSED,
534 unsigned flags UNUSED)
535 {
536 return error("packed backend cannot write objects");
odb/source.h
+2 -2
@@ -207,7 +207,7 @@ struct odb_source {
207 const void *buf, size_t len,
208 enum object_type type,
209 struct object_id *oid,
210 - struct object_id *compat_oid,
210 + const struct object_id *compat_oid,
211 enum odb_write_object_flags flags);
212
213 /*
@@ -417,7 +417,7 @@ static inline int odb_source_write_object(struct odb_source *source,
417 const void *buf, unsigned long len,
418 enum object_type type,
419 struct object_id *oid,
420 - struct object_id *compat_oid,
420 + const struct object_id *compat_oid,
421 enum odb_write_object_flags flags)
422 {
423 return source->write_object(source, buf, len, type, oid,