object-file: refactor writing objects to use loose source

The "object-file" subsystem still hosts the majority of logic used to write loose objects. Eventually, we'll want to move this logic into "odb/source-loose.c", but this isn't yet easily possible because a lot of the writing logic is still being shared with `force_object_loose()`. We will eventually detangle this logic so that we can indeed move all of it into the "loose" source. Meanwhile though, refactor the code so that it operates on a `struct odb_source_loose` directly to already make the dependency explicit. 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 b9906a645c38ef77643d661ac9a5a6aa31fbeaf4
6 files changed +53 -49
http-walker.c
+2 -1
@@ -539,8 +539,9 @@ static int fetch_object(struct walker *walker, const struct object_id *oid)
539 } else if (!oideq(&obj_req->oid, &req->real_oid)) {
540 ret = error("File %s has bad hash", hex);
541 } else if (req->rename < 0) {
542 + struct odb_source_files *files = odb_source_files_downcast(the_repository->objects->sources);
543 struct strbuf buf = STRBUF_INIT;
543 - odb_loose_path(the_repository->objects->sources, &buf, &req->oid);
544 + odb_loose_path(files->loose, &buf, &req->oid);
545 ret = error("unable to write sha1 filename %s", buf.buf);
546 strbuf_release(&buf);
547 }
http.c
+4 -2
@@ -2826,6 +2826,7 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
2826 struct http_object_request *new_http_object_request(const char *base_url,
2827 const struct object_id *oid)
2828 {
2829 + struct odb_source_files *files = odb_source_files_downcast(the_repository->objects->sources);
2830 char *hex = oid_to_hex(oid);
2831 struct strbuf filename = STRBUF_INIT;
2832 struct strbuf prevfile = STRBUF_INIT;
@@ -2840,7 +2841,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
2841 oidcpy(&freq->oid, oid);
2842 freq->localfile = -1;
2843
2843 - odb_loose_path(the_repository->objects->sources, &filename, oid);
2844 + odb_loose_path(files->loose, &filename, oid);
2845 strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
2846
2847 strbuf_addf(&prevfile, "%s.prev", filename.buf);
@@ -2966,6 +2967,7 @@ void process_http_object_request(struct http_object_request *freq)
2967
2968 int finish_http_object_request(struct http_object_request *freq)
2969 {
2970 + struct odb_source_files *files = odb_source_files_downcast(the_repository->objects->sources);
2971 struct stat st;
2972 struct strbuf filename = STRBUF_INIT;
2973
@@ -2992,7 +2994,7 @@ int finish_http_object_request(struct http_object_request *freq)
2994 unlink_or_warn(freq->tmpfile.buf);
2995 return -1;
2996 }
2995 - odb_loose_path(the_repository->objects->sources, &filename, &freq->oid);
2997 + odb_loose_path(files->loose, &filename, &freq->oid);
2998 freq->rename = finalize_object_file(the_repository, freq->tmpfile.buf, filename.buf);
2999 strbuf_release(&filename);
3000
object-file.c
+37 -38
@@ -54,14 +54,14 @@ static void fill_loose_path(struct strbuf *buf,
54 }
55 }
56
57 -const char *odb_loose_path(struct odb_source *source,
57 +const char *odb_loose_path(struct odb_source_loose *loose,
58 struct strbuf *buf,
59 const struct object_id *oid)
60 {
61 strbuf_reset(buf);
62 - strbuf_addstr(buf, source->path);
62 + strbuf_addstr(buf, loose->base.path);
63 strbuf_addch(buf, '/');
64 - fill_loose_path(buf, oid, source->odb->repo->hash_algo);
64 + fill_loose_path(buf, oid, loose->base.odb->repo->hash_algo);
65 return buf->buf;
66 }
67
@@ -575,14 +575,14 @@ static void flush_loose_object_transaction(struct odb_transaction_files *transac
575 }
576
577 /* Finalize a file on disk, and close it. */
578 -static void close_loose_object(struct odb_source *source,
578 +static void close_loose_object(struct odb_source_loose *loose,
579 int fd, const char *filename)
580 {
581 - if (source->will_destroy)
581 + if (loose->base.will_destroy)
582 goto out;
583
584 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
585 - fsync_loose_object_transaction(source->odb->transaction, fd, filename);
585 + fsync_loose_object_transaction(loose->base.odb->transaction, fd, filename);
586 else if (fsync_object_files > 0)
587 fsync_or_die(fd, filename);
588 else
@@ -651,7 +651,7 @@ static int create_tmpfile(struct repository *repo,
651 * Returns a "fd", which should later be provided to
652 * end_loose_object_common().
653 */
654 -static int start_loose_object_common(struct odb_source *source,
654 +static int start_loose_object_common(struct odb_source_loose *loose,
655 struct strbuf *tmp_file,
656 const char *filename, unsigned flags,
657 git_zstream *stream,
@@ -659,18 +659,18 @@ static int start_loose_object_common(struct odb_source *source,
659 struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
660 char *hdr, int hdrlen)
661 {
662 - const struct git_hash_algo *algo = source->odb->repo->hash_algo;
663 - const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
662 + const struct git_hash_algo *algo = loose->base.odb->repo->hash_algo;
663 + const struct git_hash_algo *compat = loose->base.odb->repo->compat_hash_algo;
664 int fd;
665
666 - fd = create_tmpfile(source->odb->repo, tmp_file, filename);
666 + fd = create_tmpfile(loose->base.odb->repo, tmp_file, filename);
667 if (fd < 0) {
668 if (flags & ODB_WRITE_OBJECT_SILENT)
669 return -1;
670 else if (errno == EACCES)
671 return error(_("insufficient permission for adding "
672 "an object to repository database %s"),
673 - source->path);
673 + loose->base.path);
674 else
675 return error_errno(
676 _("unable to create temporary file"));
@@ -700,14 +700,14 @@ static int start_loose_object_common(struct odb_source *source,
700 * Common steps for the inner git_deflate() loop for writing loose
701 * objects. Returns what git_deflate() returns.
702 */
703 -static int write_loose_object_common(struct odb_source *source,
703 +static int write_loose_object_common(struct odb_source_loose *loose,
704 struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
705 git_zstream *stream, const int flush,
706 unsigned char *in0, const int fd,
707 unsigned char *compressed,
708 const size_t compressed_len)
709 {
710 - const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
710 + const struct git_hash_algo *compat = loose->base.odb->repo->compat_hash_algo;
711 int ret;
712
713 ret = git_deflate(stream, flush ? Z_FINISH : 0);
@@ -728,12 +728,12 @@ static int write_loose_object_common(struct odb_source *source,
728 * - End the compression of zlib stream.
729 * - Get the calculated oid to "oid".
730 */
731 -static int end_loose_object_common(struct odb_source *source,
731 +static int end_loose_object_common(struct odb_source_loose *loose,
732 struct git_hash_ctx *c, struct git_hash_ctx *compat_c,
733 git_zstream *stream, struct object_id *oid,
734 struct object_id *compat_oid)
735 {
736 - const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
736 + const struct git_hash_algo *compat = loose->base.odb->repo->compat_hash_algo;
737 int ret;
738
739 ret = git_deflate_end_gently(stream);
@@ -746,7 +746,7 @@ static int end_loose_object_common(struct odb_source *source,
746 return Z_OK;
747 }
748
749 -int write_loose_object(struct odb_source *source,
749 +int write_loose_object(struct odb_source_loose *loose,
750 const struct object_id *oid, char *hdr,
751 int hdrlen, const void *buf, unsigned long len,
752 time_t mtime, unsigned flags)
@@ -760,11 +760,11 @@ int write_loose_object(struct odb_source *source,
760 static struct strbuf filename = STRBUF_INIT;
761
762 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
763 - prepare_loose_object_transaction(source->odb->transaction);
763 + prepare_loose_object_transaction(loose->base.odb->transaction);
764
765 - odb_loose_path(source, &filename, oid);
765 + odb_loose_path(loose, &filename, oid);
766
767 - fd = start_loose_object_common(source, &tmp_file, filename.buf, flags,
767 + fd = start_loose_object_common(loose, &tmp_file, filename.buf, flags,
768 &stream, compressed, sizeof(compressed),
769 &c, NULL, hdr, hdrlen);
770 if (fd < 0)
@@ -776,14 +776,14 @@ int write_loose_object(struct odb_source *source,
776 do {
777 unsigned char *in0 = stream.next_in;
778
779 - ret = write_loose_object_common(source, &c, NULL, &stream, 1, in0, fd,
779 + ret = write_loose_object_common(loose, &c, NULL, &stream, 1, in0, fd,
780 compressed, sizeof(compressed));
781 } while (ret == Z_OK);
782
783 if (ret != Z_STREAM_END)
784 die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
785 ret);
786 - ret = end_loose_object_common(source, &c, NULL, &stream, &parano_oid, NULL);
786 + ret = end_loose_object_common(loose, &c, NULL, &stream, &parano_oid, NULL);
787 if (ret != Z_OK)
788 die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
789 ret);
@@ -791,7 +791,7 @@ int write_loose_object(struct odb_source *source,
791 die(_("confused by unstable object source data for %s"),
792 oid_to_hex(oid));
793
794 - close_loose_object(source, fd, tmp_file.buf);
794 + close_loose_object(loose, fd, tmp_file.buf);
795
796 if (mtime) {
797 struct utimbuf utb;
@@ -802,16 +802,15 @@ int write_loose_object(struct odb_source *source,
802 warning_errno(_("failed utime() on %s"), tmp_file.buf);
803 }
804
805 - return finalize_object_file_flags(source->odb->repo, tmp_file.buf, filename.buf,
805 + return finalize_object_file_flags(loose->base.odb->repo, tmp_file.buf, filename.buf,
806 FOF_SKIP_COLLISION_CHECK);
807 }
808
809 -int odb_source_loose_write_stream(struct odb_source *source,
809 +int odb_source_loose_write_stream(struct odb_source_loose *loose,
810 struct odb_write_stream *in_stream, size_t len,
811 struct object_id *oid)
812 {
813 - struct odb_source_files *files = odb_source_files_downcast(source);
814 - const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
813 + const struct git_hash_algo *compat = loose->base.odb->repo->compat_hash_algo;
814 struct object_id compat_oid;
815 int fd, ret, err = 0, flush = 0;
816 unsigned char compressed[4096];
@@ -825,10 +824,10 @@ int odb_source_loose_write_stream(struct odb_source *source,
824 int hdrlen;
825
826 if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT))
828 - prepare_loose_object_transaction(source->odb->transaction);
827 + prepare_loose_object_transaction(loose->base.odb->transaction);
828
829 /* Since oid is not determined, save tmp file to odb path. */
831 - strbuf_addf(&filename, "%s/", source->path);
830 + strbuf_addf(&filename, "%s/", loose->base.path);
831 hdrlen = format_object_header(hdr, sizeof(hdr), OBJ_BLOB, len);
832
833 /*
@@ -839,7 +838,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
838 * - Setup zlib stream for compression.
839 * - Start to feed header to zlib stream.
840 */
842 - fd = start_loose_object_common(source, &tmp_file, filename.buf, 0,
841 + fd = start_loose_object_common(loose, &tmp_file, filename.buf, 0,
842 &stream, compressed, sizeof(compressed),
843 &c, &compat_c, hdr, hdrlen);
844 if (fd < 0) {
@@ -867,7 +866,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
866 if (in_stream->is_finished)
867 flush = 1;
868 }
870 - ret = write_loose_object_common(source, &c, &compat_c, &stream, flush, in0, fd,
869 + ret = write_loose_object_common(loose, &c, &compat_c, &stream, flush, in0, fd,
870 compressed, sizeof(compressed));
871 /*
872 * Unlike write_loose_object(), we do not have the entire
@@ -890,16 +889,16 @@ int odb_source_loose_write_stream(struct odb_source *source,
889 */
890 if (ret != Z_STREAM_END)
891 die(_("unable to stream deflate new object (%d)"), ret);
893 - ret = end_loose_object_common(source, &c, &compat_c, &stream, oid, &compat_oid);
892 + ret = end_loose_object_common(loose, &c, &compat_c, &stream, oid, &compat_oid);
893 if (ret != Z_OK)
894 die(_("deflateEnd on stream object failed (%d)"), ret);
896 - close_loose_object(source, fd, tmp_file.buf);
895 + close_loose_object(loose, fd, tmp_file.buf);
896
898 - if (odb_freshen_object(source->odb, oid)) {
897 + if (odb_freshen_object(loose->base.odb, oid)) {
898 unlink_or_warn(tmp_file.buf);
899 goto cleanup;
900 }
902 - odb_loose_path(source, &filename, oid);
901 + odb_loose_path(loose, &filename, oid);
902
903 /* We finally know the object path, and create the missing dir. */
904 dirlen = directory_size(filename.buf);
@@ -907,7 +906,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
906 struct strbuf dir = STRBUF_INIT;
907 strbuf_add(&dir, filename.buf, dirlen);
908
910 - if (safe_create_dir_in_gitdir(source->odb->repo, dir.buf) &&
909 + if (safe_create_dir_in_gitdir(loose->base.odb->repo, dir.buf) &&
910 errno != EEXIST) {
911 err = error_errno(_("unable to create directory %s"), dir.buf);
912 strbuf_release(&dir);
@@ -916,10 +915,10 @@ int odb_source_loose_write_stream(struct odb_source *source,
915 strbuf_release(&dir);
916 }
917
919 - err = finalize_object_file_flags(source->odb->repo, tmp_file.buf, filename.buf,
918 + err = finalize_object_file_flags(loose->base.odb->repo, tmp_file.buf, filename.buf,
919 FOF_SKIP_COLLISION_CHECK);
920 if (!err && compat)
922 - err = repo_add_loose_object_map(files->loose, oid, &compat_oid);
921 + err = repo_add_loose_object_map(loose, oid, &compat_oid);
922 cleanup:
923 strbuf_release(&tmp_file);
924 strbuf_release(&filename);
@@ -957,7 +956,7 @@ int force_object_loose(struct odb_source *source,
956 oid_to_hex(oid), compat->name);
957 }
958 hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
960 - ret = write_loose_object(source, oid, hdr, hdrlen, buf, len, mtime, 0);
959 + ret = write_loose_object(files->loose, oid, hdr, hdrlen, buf, len, mtime, 0);
960 if (!ret && compat)
961 ret = repo_add_loose_object_map(files->loose, oid, &compat_oid);
962 free(buf);
object-file.h
+3 -3
@@ -23,7 +23,7 @@ int index_path(struct index_state *istate, struct object_id *oid, const char *pa
23 struct object_info;
24 struct odb_source;
25
26 -int odb_source_loose_write_stream(struct odb_source *source,
26 +int odb_source_loose_write_stream(struct odb_source_loose *loose,
27 struct odb_write_stream *stream, size_t len,
28 struct object_id *oid);
29
@@ -31,7 +31,7 @@ int odb_source_loose_write_stream(struct odb_source *source,
31 * Put in `buf` the name of the file in the local object database that
32 * would be used to store a loose object with the specified oid.
33 */
34 -const char *odb_loose_path(struct odb_source *source,
34 +const char *odb_loose_path(struct odb_source_loose *source,
35 struct strbuf *buf,
36 const struct object_id *oid);
37
@@ -127,7 +127,7 @@ void write_object_file_prepare(const struct git_hash_algo *algo,
127 const void *buf, unsigned long len,
128 enum object_type type, struct object_id *oid,
129 char *hdr, int *hdrlen);
130 -int write_loose_object(struct odb_source *source,
130 +int write_loose_object(struct odb_source_loose *loose,
131 const struct object_id *oid, char *hdr,
132 int hdrlen, const void *buf, unsigned long len,
133 time_t mtime, unsigned flags);
odb/source-files.c
+2 -1
@@ -174,7 +174,8 @@ static int odb_source_files_write_object_stream(struct odb_source *source,
174 size_t len,
175 struct object_id *oid)
176 {
177 - return odb_source_loose_write_stream(source, stream, len, oid);
177 + struct odb_source_files *files = odb_source_files_downcast(source);
178 + return odb_source_loose_write_stream(files->loose, stream, len, oid);
179 }
180
181 static int odb_source_files_begin_transaction(struct odb_source *source,
odb/source-loose.c
+5 -4
@@ -220,7 +220,7 @@ static int odb_source_loose_read_object_info(struct odb_source *source,
220 if (flags & OBJECT_INFO_SECOND_READ)
221 return -1;
222
223 - odb_loose_path(source, &buf, oid);
223 + odb_loose_path(loose, &buf, oid);
224 return read_object_info_from_path(loose, buf.buf, oid, oi, flags);
225 }
226
@@ -238,7 +238,7 @@ static int open_loose_object(struct odb_source_loose *loose,
238 static struct strbuf buf = STRBUF_INIT;
239 int fd;
240
241 - *path = odb_loose_path(&loose->base, &buf, oid);
241 + *path = odb_loose_path(loose, &buf, oid);
242 fd = git_open(*path);
243 if (fd >= 0)
244 return fd;
@@ -584,8 +584,9 @@ out:
584 static int odb_source_loose_freshen_object(struct odb_source *source,
585 const struct object_id *oid)
586 {
587 + struct odb_source_loose *loose = odb_source_loose_downcast(source);
588 static struct strbuf path = STRBUF_INIT;
588 - odb_loose_path(source, &path, oid);
589 + odb_loose_path(loose, &path, oid);
590 return !!check_and_freshen_file(path.buf, 1);
591 }
592
@@ -624,7 +625,7 @@ static int odb_source_loose_write_object(struct odb_source *source,
625 write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
626 if (odb_freshen_object(source->odb, oid))
627 return 0;
627 - if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
628 + if (write_loose_object(loose, oid, hdr, hdrlen, buf, len, 0, flags))
629 return -1;
630 if (compat)
631 return repo_add_loose_object_map(loose, oid, &compat_oid);