{"thread":{"id":"63270","subject":"[PATCH 0/9] Split up \"object-file.c\"","startedAt":"2025-04-08T10:24:16Z","lastAt":"2025-04-16T16:21:13Z","messageCount":53,"participants":["Patrick Steinhardt","Junio C Hamano","Elijah Newren","Eric Sunshine","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"515849","messageId":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","threadId":"63270","inReplyTo":null,"subject":"[PATCH 0/9] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:08Z","receivedAt":"2025-04-08T10:24:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\n\"object-file.c\" is quite a grab-bag of all kinds of different functions.\nMany of these functions aren't really a good fit though and should be\nowned by a different subsystem. This patch series tries to split up\nconcerns a bit better by splitting out this functionality into other\nfiles:\n\n  - `safe_create_leading_directories()` is moved into \"dir.c\".\n  - `xmmap()` is moved into \"wrapper.c\".\n  - `git_open_cloexec()` is moved into \"compat/open.c\".\n  - Several functions attached to `struct index_state` are moved into\n    \"read-cache.c\".\n  - Several functions related to `struct object_store` are moved into a\n    new file \"object-store.c\".\n\n\"object-file.c\" now mostly contains logic to read and write loose object\nfiles, whereas \"object-store.c\" contains the higher-level logic to\nmanage different object directories for a repository. Eventually, these\nwill become the loose object backend as well as the `struct ref_store`\nequivalent for objects, respectively.\n\nThe series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\nwith ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\nbranch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\ninto it.\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (9):\n      object-file: move `safe_create_leading_directories()` into \"dir.c\"\n      object-file: move `git_open_cloexec()` to \"compat/open.c\"\n      object-file: move `xmmap()` into \"wrapper.c\"\n      object-file: split out functions relating to object store subsystem\n      object-file: split up concerns of `HASH_*` flags\n      object-file: split out functions relating to index subsystem\n      object: split out functions relating to object store subsystem\n      object-store: remove global array of cached objects\n      object-store: merge \"object-store-ll.h\" and \"object-store.h\"\n\n Makefile                           |    3 +-\n apply.c                            |    2 +-\n archive-tar.c                      |    2 +-\n archive-zip.c                      |    2 +-\n archive.c                          |    2 +-\n attr.c                             |    2 +-\n bisect.c                           |    2 +-\n blame.c                            |    4 +-\n builtin/backfill.c                 |    2 +-\n builtin/blame.c                    |    2 +-\n builtin/bugreport.c                |    2 +-\n builtin/cat-file.c                 |    2 +-\n builtin/checkout.c                 |    3 +-\n builtin/clone.c                    |    2 +-\n builtin/commit-graph.c             |    2 +-\n builtin/commit-tree.c              |    2 +-\n builtin/count-objects.c            |    2 +-\n builtin/credential-cache--daemon.c |    2 +-\n builtin/describe.c                 |    2 +-\n builtin/diagnose.c                 |    2 +-\n builtin/difftool.c                 |    4 +-\n builtin/fast-export.c              |    2 +-\n builtin/fast-import.c              |    2 +-\n builtin/fetch.c                    |    2 +-\n builtin/fsck.c                     |    3 +-\n builtin/gc.c                       |    4 +-\n builtin/grep.c                     |    2 +-\n builtin/hash-object.c              |   26 +-\n builtin/index-pack.c               |    2 +-\n builtin/init-db.c                  |    2 +-\n builtin/log.c                      |    4 +-\n builtin/ls-tree.c                  |    2 +-\n builtin/merge-file.c               |    1 +\n builtin/merge-tree.c               |    2 +-\n builtin/mktag.c                    |    2 +-\n builtin/mktree.c                   |    3 +-\n builtin/multi-pack-index.c         |    2 +-\n builtin/notes.c                    |    3 +-\n builtin/pack-objects.c             |    2 +-\n builtin/pack-redundant.c           |    2 +-\n builtin/prune.c                    |    2 +-\n builtin/receive-pack.c             |    3 +-\n builtin/remote.c                   |    2 +-\n builtin/repack.c                   |    2 +-\n builtin/replace.c                  |    5 +-\n builtin/rev-list.c                 |    2 +-\n builtin/show-ref.c                 |    2 +-\n builtin/submodule--helper.c        |    2 +-\n builtin/tag.c                      |    3 +-\n builtin/unpack-file.c              |    3 +-\n builtin/unpack-objects.c           |    3 +-\n builtin/update-index.c             |    2 +-\n bulk-checkin.c                     |    9 +-\n bundle-uri.c                       |    2 +-\n bundle.c                           |    2 +-\n cache-tree.c                       |    4 +-\n combine-diff.c                     |    2 +-\n commit-graph.c                     |    4 +-\n commit-graph.h                     |    2 +-\n commit.c                           |    3 +-\n compat/open.c                      |   29 +\n config.c                           |    2 +-\n connected.c                        |    2 +-\n convert.c                          |    2 +-\n diagnose.c                         |    2 +-\n diff.c                             |    4 +-\n diffcore-rename.c                  |    2 +-\n dir.c                              |  109 ++-\n dir.h                              |   35 +\n entry.c                            |    2 +-\n fetch-pack.c                       |    2 +-\n fmt-merge-msg.c                    |    2 +-\n fsck.c                             |    2 +-\n git-compat-util.h                  |    3 +\n grep.c                             |    2 +-\n http-backend.c                     |    2 +-\n http-push.c                        |    3 +-\n http-walker.c                      |    2 +-\n http.c                             |    2 +-\n list-objects-filter.c              |    2 +-\n list-objects.c                     |    2 +-\n log-tree.c                         |    2 +-\n mailmap.c                          |    2 +-\n match-trees.c                      |    3 +-\n merge-blobs.c                      |    2 +-\n merge-ort.c                        |    3 +-\n merge-recursive.c                  |    2 +-\n meson.build                        |    2 +\n midx-write.c                       |    1 +\n midx.c                             |    1 -\n notes-cache.c                      |    3 +-\n notes-merge.c                      |    5 +-\n notes.c                            |    3 +-\n object-file.c                      | 1420 +-----------------------------------\n object-file.h                      |  118 +--\n object-name.c                      |    2 +-\n object-store-ll.h                  |  556 --------------\n object-store.c                     | 1050 ++++++++++++++++++++++++++\n object-store.h                     |  516 ++++++++++++-\n object.c                           |   67 --\n oss-fuzz/fuzz-pack-idx.c           |    2 +-\n pack-bitmap-write.c                |    2 +-\n pack-bitmap.c                      |    3 +-\n pack-check.c                       |    2 +-\n pack-mtimes.c                      |    3 +-\n pack-objects.h                     |    2 +-\n pack-revindex.c                    |    3 +-\n packfile.c                         |    2 +-\n path.c                             |    2 +-\n promisor-remote.c                  |    2 +-\n protocol-caps.c                    |    2 +-\n prune-packed.c                     |    2 +-\n reachable.c                        |    2 +-\n read-cache.c                       |  230 +++++-\n read-cache.h                       |    9 +\n ref-filter.c                       |    2 +-\n reflog.c                           |    2 +-\n refs.c                             |    2 +-\n remote.c                           |    2 +-\n replace-object.c                   |    2 +-\n replace-object.h                   |    2 +-\n repository.c                       |    2 +-\n rerere.c                           |    2 +-\n revision.c                         |    2 +-\n send-pack.c                        |    2 +-\n sequencer.c                        |    2 +-\n server-info.c                      |    2 +-\n shallow.c                          |    2 +-\n streaming.c                        |    2 +-\n submodule-config.c                 |    2 +-\n submodule.c                        |    2 +-\n t/helper/test-pack-mtimes.c        |    2 +-\n t/helper/test-partial-clone.c      |    2 +-\n t/helper/test-read-graph.c         |    2 +-\n t/helper/test-read-midx.c          |    2 +-\n t/helper/test-ref-store.c          |    2 +-\n tag.c                              |    2 +-\n tmp-objdir.c                       |    2 +-\n tree-walk.c                        |    2 +-\n tree.c                             |    2 +-\n unpack-trees.c                     |    2 +-\n upload-pack.c                      |    2 +-\n walker.c                           |    2 +-\n wrapper.c                          |   48 ++\n xdiff-interface.c                  |    2 +-\n 145 files changed, 2296 insertions(+), 2224 deletions(-)\n\n\n---\nbase-commit: 9442b1c919af9aed513eb0a484fe96358a500cf5\nchange-id: 20250408-pks-split-object-file-c61d7cd2a21f\n\n"},{"id":"515851","messageId":"20250408-pks-split-object-file-v1-1-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:09Z","receivedAt":"2025-04-08T10:24:16Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `safe_create_leading_directories()` function and its relatives are\nlocated in \"object-file.c\", which is not a good fit as they provide\ngeneric functionality not related to objects at all. Move them into\n\"dir.c\".\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/bugreport.c                |   2 +-\n builtin/credential-cache--daemon.c |   2 +-\n builtin/diagnose.c                 |   2 +-\n builtin/fsck.c                     |   1 +\n builtin/gc.c                       |   2 +-\n builtin/init-db.c                  |   2 +-\n builtin/log.c                      |   2 +-\n commit-graph.c                     |   1 +\n dir.c                              | 107 ++++++++++++++++++++++++++++++++++++-\n dir.h                              |  35 ++++++++++++\n midx-write.c                       |   1 +\n object-file.c                      | 106 ------------------------------------\n object-file.h                      |  35 ------------\n 13 files changed, 150 insertions(+), 148 deletions(-)\n\ndiff --git a/builtin/bugreport.c b/builtin/bugreport.c\nindex 66d64bfd5ae..d07fa91c247 100644\n--- a/builtin/bugreport.c\n+++ b/builtin/bugreport.c\n@@ -1,6 +1,7 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"editor.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n@@ -10,7 +11,6 @@\n #include \"hook.h\"\n #include \"hook-list.h\"\n #include \"diagnose.h\"\n-#include \"object-file.h\"\n #include \"setup.h\"\n #include \"version.h\"\n \ndiff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c\nindex e707618e743..80d29b4f5c0 100644\n--- a/builtin/credential-cache--daemon.c\n+++ b/builtin/credential-cache--daemon.c\n@@ -1,8 +1,8 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n \n #ifndef NO_UNIX_SOCKETS\ndiff --git a/builtin/diagnose.c b/builtin/diagnose.c\nindex 33c39bd5981..d5dadd6a48b 100644\n--- a/builtin/diagnose.c\n+++ b/builtin/diagnose.c\n@@ -2,8 +2,8 @@\n \n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n #include \"diagnose.h\"\n \ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 9c8a6d6a8df..32d40d8f9fc 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -1,5 +1,6 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"config.h\"\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 99431fd4674..b069629676c 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -16,6 +16,7 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"date.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"config.h\"\n@@ -28,7 +29,6 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 196dccdd77a..39730c1b0ce 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -6,9 +6,9 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"refs.h\"\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 0d4c579dad7..06ffaa93e86 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -10,11 +10,11 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n-#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"pager.h\"\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 8286d5dda24..3fae20dc21b 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -4,6 +4,7 @@\n #include \"git-compat-util.h\"\n #include \"config.h\"\n #include \"csum-file.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\ndiff --git a/dir.c b/dir.c\nindex 28b0e03feb4..16ae3b5169d 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,6 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\n #include \"refs.h\"\n@@ -4132,3 +4131,109 @@ int path_match_flags(const char *const str, const enum path_match_flags flags)\n \t\treturn is_xplatform_dir_sep(*p);\n \tBUG(\"unreachable\");\n }\n+\n+int mkdir_in_gitdir(const char *path)\n+{\n+\tif (mkdir(path, 0777)) {\n+\t\tint saved_errno = errno;\n+\t\tstruct stat st;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (errno != EEXIST)\n+\t\t\treturn -1;\n+\t\t/*\n+\t\t * Are we looking at a path in a symlinked worktree\n+\t\t * whose original repository does not yet have it?\n+\t\t * e.g. .git/rr-cache pointing at its original\n+\t\t * repository in which the user hasn't performed any\n+\t\t * conflict resolution yet?\n+\t\t */\n+\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n+\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n+\t\t    !is_absolute_path(sb.buf) ||\n+\t\t    mkdir(sb.buf, 0777)) {\n+\t\t\tstrbuf_release(&sb);\n+\t\t\terrno = saved_errno;\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrbuf_release(&sb);\n+\t}\n+\treturn adjust_shared_perm(the_repository, path);\n+}\n+\n+static enum scld_error safe_create_leading_directories_1(char *path, int share)\n+{\n+\tchar *next_component = path + offset_1st_component(path);\n+\tenum scld_error ret = SCLD_OK;\n+\n+\twhile (ret == SCLD_OK && next_component) {\n+\t\tstruct stat st;\n+\t\tchar *slash = next_component, slash_character;\n+\n+\t\twhile (*slash && !is_dir_sep(*slash))\n+\t\t\tslash++;\n+\n+\t\tif (!*slash)\n+\t\t\tbreak;\n+\n+\t\tnext_component = slash + 1;\n+\t\twhile (is_dir_sep(*next_component))\n+\t\t\tnext_component++;\n+\t\tif (!*next_component)\n+\t\t\tbreak;\n+\n+\t\tslash_character = *slash;\n+\t\t*slash = '\\0';\n+\t\tif (!stat(path, &st)) {\n+\t\t\t/* path exists */\n+\t\t\tif (!S_ISDIR(st.st_mode)) {\n+\t\t\t\terrno = ENOTDIR;\n+\t\t\t\tret = SCLD_EXISTS;\n+\t\t\t}\n+\t\t} else if (mkdir(path, 0777)) {\n+\t\t\tif (errno == EEXIST &&\n+\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t; /* somebody created it since we checked */\n+\t\t\telse if (errno == ENOENT)\n+\t\t\t\t/*\n+\t\t\t\t * Either mkdir() failed because\n+\t\t\t\t * somebody just pruned the containing\n+\t\t\t\t * directory, or stat() failed because\n+\t\t\t\t * the file that was in our way was\n+\t\t\t\t * just removed.  Either way, inform\n+\t\t\t\t * the caller that it might be worth\n+\t\t\t\t * trying again:\n+\t\t\t\t */\n+\t\t\t\tret = SCLD_VANISHED;\n+\t\t\telse\n+\t\t\t\tret = SCLD_FAILED;\n+\t\t} else if (share && adjust_shared_perm(the_repository, path)) {\n+\t\t\tret = SCLD_PERMS;\n+\t\t}\n+\t\t*slash = slash_character;\n+\t}\n+\treturn ret;\n+}\n+\n+enum scld_error safe_create_leading_directories(char *path)\n+{\n+\treturn safe_create_leading_directories_1(path, 1);\n+}\n+\n+enum scld_error safe_create_leading_directories_no_share(char *path)\n+{\n+\treturn safe_create_leading_directories_1(path, 0);\n+}\n+\n+enum scld_error safe_create_leading_directories_const(const char *path)\n+{\n+\tint save_errno;\n+\t/* path points to cache entries, so xstrdup before messing with it */\n+\tchar *buf = xstrdup(path);\n+\tenum scld_error result = safe_create_leading_directories(buf);\n+\n+\tsave_errno = errno;\n+\tfree(buf);\n+\terrno = save_errno;\n+\treturn result;\n+}\ndiff --git a/dir.h b/dir.h\nindex d7e71aa8daa..02c1f9420b0 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -676,4 +676,39 @@ static inline int starts_with_dot_dot_slash_native(const char *const path)\n \treturn path_match_flags(path, what | PATH_MATCH_NATIVE);\n }\n \n+/*\n+ * Create the directory containing the named path, using care to be\n+ * somewhat safe against races. Return one of the scld_error values to\n+ * indicate success/failure. On error, set errno to describe the\n+ * problem.\n+ *\n+ * SCLD_VANISHED indicates that one of the ancestor directories of the\n+ * path existed at one point during the function call and then\n+ * suddenly vanished, probably because another process pruned the\n+ * directory while we were working.  To be robust against this kind of\n+ * race, callers might want to try invoking the function again when it\n+ * returns SCLD_VANISHED.\n+ *\n+ * safe_create_leading_directories() temporarily changes path while it\n+ * is working but restores it before returning.\n+ * safe_create_leading_directories_const() doesn't modify path, even\n+ * temporarily. Both these variants adjust the permissions of the\n+ * created directories to honor core.sharedRepository, so they are best\n+ * suited for files inside the git dir. For working tree files, use\n+ * safe_create_leading_directories_no_share() instead, as it ignores\n+ * the core.sharedRepository setting.\n+ */\n+enum scld_error {\n+\tSCLD_OK = 0,\n+\tSCLD_FAILED = -1,\n+\tSCLD_PERMS = -2,\n+\tSCLD_EXISTS = -3,\n+\tSCLD_VANISHED = -4\n+};\n+enum scld_error safe_create_leading_directories(char *path);\n+enum scld_error safe_create_leading_directories_const(const char *path);\n+enum scld_error safe_create_leading_directories_no_share(char *path);\n+\n+int mkdir_in_gitdir(const char *path);\n+\n #endif\ndiff --git a/midx-write.c b/midx-write.c\nindex a628ac24dcb..e01a867c583 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -3,6 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n #include \"packfile.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 772c311f188..23b2c8560be 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -91,112 +91,6 @@ static int get_conv_flags(unsigned flags)\n }\n \n \n-int mkdir_in_gitdir(const char *path)\n-{\n-\tif (mkdir(path, 0777)) {\n-\t\tint saved_errno = errno;\n-\t\tstruct stat st;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (errno != EEXIST)\n-\t\t\treturn -1;\n-\t\t/*\n-\t\t * Are we looking at a path in a symlinked worktree\n-\t\t * whose original repository does not yet have it?\n-\t\t * e.g. .git/rr-cache pointing at its original\n-\t\t * repository in which the user hasn't performed any\n-\t\t * conflict resolution yet?\n-\t\t */\n-\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n-\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n-\t\t    !is_absolute_path(sb.buf) ||\n-\t\t    mkdir(sb.buf, 0777)) {\n-\t\t\tstrbuf_release(&sb);\n-\t\t\terrno = saved_errno;\n-\t\t\treturn -1;\n-\t\t}\n-\t\tstrbuf_release(&sb);\n-\t}\n-\treturn adjust_shared_perm(the_repository, path);\n-}\n-\n-static enum scld_error safe_create_leading_directories_1(char *path, int share)\n-{\n-\tchar *next_component = path + offset_1st_component(path);\n-\tenum scld_error ret = SCLD_OK;\n-\n-\twhile (ret == SCLD_OK && next_component) {\n-\t\tstruct stat st;\n-\t\tchar *slash = next_component, slash_character;\n-\n-\t\twhile (*slash && !is_dir_sep(*slash))\n-\t\t\tslash++;\n-\n-\t\tif (!*slash)\n-\t\t\tbreak;\n-\n-\t\tnext_component = slash + 1;\n-\t\twhile (is_dir_sep(*next_component))\n-\t\t\tnext_component++;\n-\t\tif (!*next_component)\n-\t\t\tbreak;\n-\n-\t\tslash_character = *slash;\n-\t\t*slash = '\\0';\n-\t\tif (!stat(path, &st)) {\n-\t\t\t/* path exists */\n-\t\t\tif (!S_ISDIR(st.st_mode)) {\n-\t\t\t\terrno = ENOTDIR;\n-\t\t\t\tret = SCLD_EXISTS;\n-\t\t\t}\n-\t\t} else if (mkdir(path, 0777)) {\n-\t\t\tif (errno == EEXIST &&\n-\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n-\t\t\t\t; /* somebody created it since we checked */\n-\t\t\telse if (errno == ENOENT)\n-\t\t\t\t/*\n-\t\t\t\t * Either mkdir() failed because\n-\t\t\t\t * somebody just pruned the containing\n-\t\t\t\t * directory, or stat() failed because\n-\t\t\t\t * the file that was in our way was\n-\t\t\t\t * just removed.  Either way, inform\n-\t\t\t\t * the caller that it might be worth\n-\t\t\t\t * trying again:\n-\t\t\t\t */\n-\t\t\t\tret = SCLD_VANISHED;\n-\t\t\telse\n-\t\t\t\tret = SCLD_FAILED;\n-\t\t} else if (share && adjust_shared_perm(the_repository, path)) {\n-\t\t\tret = SCLD_PERMS;\n-\t\t}\n-\t\t*slash = slash_character;\n-\t}\n-\treturn ret;\n-}\n-\n-enum scld_error safe_create_leading_directories(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 1);\n-}\n-\n-enum scld_error safe_create_leading_directories_no_share(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 0);\n-}\n-\n-enum scld_error safe_create_leading_directories_const(const char *path)\n-{\n-\tint save_errno;\n-\t/* path points to cache entries, so xstrdup before messing with it */\n-\tchar *buf = xstrdup(path);\n-\tenum scld_error result = safe_create_leading_directories(buf);\n-\n-\tsave_errno = errno;\n-\tfree(buf);\n-\terrno = save_errno;\n-\treturn result;\n-}\n-\n int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n {\n \tint fd;\ndiff --git a/object-file.h b/object-file.h\nindex 81b30d269c8..922f2bba8c9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,41 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-/*\n- * Create the directory containing the named path, using care to be\n- * somewhat safe against races. Return one of the scld_error values to\n- * indicate success/failure. On error, set errno to describe the\n- * problem.\n- *\n- * SCLD_VANISHED indicates that one of the ancestor directories of the\n- * path existed at one point during the function call and then\n- * suddenly vanished, probably because another process pruned the\n- * directory while we were working.  To be robust against this kind of\n- * race, callers might want to try invoking the function again when it\n- * returns SCLD_VANISHED.\n- *\n- * safe_create_leading_directories() temporarily changes path while it\n- * is working but restores it before returning.\n- * safe_create_leading_directories_const() doesn't modify path, even\n- * temporarily. Both these variants adjust the permissions of the\n- * created directories to honor core.sharedRepository, so they are best\n- * suited for files inside the git dir. For working tree files, use\n- * safe_create_leading_directories_no_share() instead, as it ignores\n- * the core.sharedRepository setting.\n- */\n-enum scld_error {\n-\tSCLD_OK = 0,\n-\tSCLD_FAILED = -1,\n-\tSCLD_PERMS = -2,\n-\tSCLD_EXISTS = -3,\n-\tSCLD_VANISHED = -4\n-};\n-enum scld_error safe_create_leading_directories(char *path);\n-enum scld_error safe_create_leading_directories_const(const char *path);\n-enum scld_error safe_create_leading_directories_no_share(char *path);\n-\n-int mkdir_in_gitdir(const char *path);\n-\n int git_open_cloexec(const char *name, int flags);\n #define git_open(name) git_open_cloexec(name, O_RDONLY)\n \n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515852","messageId":"20250408-pks-split-object-file-v1-2-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 2/9] object-file: move `git_open_cloexec()` to \"compat/open.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:10Z","receivedAt":"2025-04-08T10:24:17Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `git_open_cloexec()` wrapper function provides the ability to open a\nfile with `O_CLOEXEC` in a platform-agnostic way. This function is\nprovided by \"object-file.c\" even though it is not specific to the object\nsubsystem at all.\n\nMove the file into \"compat/open.c\". This file already exists before this\ncommit, but has only been compiled conditionally depending on whether or\nnot open(3p) may return EINTR. With this change we now unconditionally\ncompile the object, but wrap `git_open_with_retry()` in an ifdef.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile          |  2 +-\n commit-graph.c    |  1 -\n compat/open.c     | 29 +++++++++++++++++++++++++++++\n git-compat-util.h |  3 +++\n meson.build       |  1 +\n midx.c            |  1 -\n object-file.c     | 27 ---------------------------\n object-file.h     |  3 ---\n pack-bitmap.c     |  1 -\n pack-mtimes.c     |  1 -\n pack-revindex.c   |  1 -\n 11 files changed, 34 insertions(+), 36 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex c41fc41ef0e..bb5407b4703 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -994,6 +994,7 @@ LIB_OBJS += common-exit.o\n LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n+LIB_OBJS += compat/open.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += compiler-tricks/not-constant.o\n LIB_OBJS += config.o\n@@ -1812,7 +1813,6 @@ ifdef FREAD_READS_DIRECTORIES\n endif\n ifdef OPEN_RETURNS_EINTR\n \tCOMPAT_CFLAGS += -DOPEN_RETURNS_EINTR\n-\tCOMPAT_OBJS += compat/open.o\n endif\n ifdef NO_SYMLINK_HEAD\n \tBASIC_CFLAGS += -DNO_SYMLINK_HEAD\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 3fae20dc21b..8060c358b84 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -14,7 +14,6 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\ndiff --git a/compat/open.c b/compat/open.c\nindex eb3754a23b8..37ae2b1aeb9 100644\n--- a/compat/open.c\n+++ b/compat/open.c\n@@ -1,5 +1,6 @@\n #include \"git-compat-util.h\"\n \n+#ifdef OPEN_RETURNS_EINTR\n #undef open\n int git_open_with_retry(const char *path, int flags, ...)\n {\n@@ -23,3 +24,31 @@ int git_open_with_retry(const char *path, int flags, ...)\n \n \treturn ret;\n }\n+#endif\n+\n+int git_open_cloexec(const char *name, int flags)\n+{\n+\tint fd;\n+\tstatic int o_cloexec = O_CLOEXEC;\n+\n+\tfd = open(name, flags | o_cloexec);\n+\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n+\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n+\t\to_cloexec &= ~O_CLOEXEC;\n+\t\tfd = open(name, flags | o_cloexec);\n+\t}\n+\n+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n+\t{\n+\t\tstatic int fd_cloexec = FD_CLOEXEC;\n+\n+\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n+\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n+\t\t\tint flags = fcntl(fd, F_GETFD);\n+\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n+\t\t\t\tfd_cloexec = 0;\n+\t\t}\n+\t}\n+#endif\n+\treturn fd;\n+}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex cf733b38acd..9273a8ee087 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1000,6 +1000,9 @@ int git_vsnprintf(char *str, size_t maxsize,\n int git_open_with_retry(const char *path, int flag, ...);\n #endif\n \n+int git_open_cloexec(const char *name, int flags);\n+#define git_open(name) git_open_cloexec(name, O_RDONLY)\n+\n #ifdef __GLIBC_PREREQ\n #if __GLIBC_PREREQ(2, 1)\n #define HAVE_STRCHRNUL\ndiff --git a/meson.build b/meson.build\nindex 145d2f7ff9e..a55e800b85b 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -263,6 +263,7 @@ libgit_sources = [\n   'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n+  'compat/open.c',\n   'compat/terminal.c',\n   'compiler-tricks/not-constant.c',\n   'config.c',\ndiff --git a/midx.c b/midx.c\nindex 807fdf72f7b..3d0015f7828 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -5,7 +5,6 @@\n #include \"dir.h\"\n #include \"hex.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"hash-lookup.h\"\n #include \"midx.h\"\n #include \"progress.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 23b2c8560be..1a20c7fa072 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -834,33 +834,6 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \treturn !oideq(oid, &real_oid) ? -1 : 0;\n }\n \n-int git_open_cloexec(const char *name, int flags)\n-{\n-\tint fd;\n-\tstatic int o_cloexec = O_CLOEXEC;\n-\n-\tfd = open(name, flags | o_cloexec);\n-\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n-\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n-\t\to_cloexec &= ~O_CLOEXEC;\n-\t\tfd = open(name, flags | o_cloexec);\n-\t}\n-\n-#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n-\t{\n-\t\tstatic int fd_cloexec = FD_CLOEXEC;\n-\n-\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n-\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n-\t\t\tint flags = fcntl(fd, F_GETFD);\n-\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n-\t\t\t\tfd_cloexec = 0;\n-\t\t}\n-\t}\n-#endif\n-\treturn fd;\n-}\n-\n /*\n  * Find \"oid\" as a loose object in the local repository or in an alternate.\n  * Returns 0 on success, negative on failure.\ndiff --git a/object-file.h b/object-file.h\nindex 922f2bba8c9..353d8a85c33 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,9 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-int git_open_cloexec(const char *name, int flags);\n-#define git_open(name) git_open_cloexec(name, O_RDONLY)\n-\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 7fd78c634ef..0dbd7c4ffe1 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,6 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex cdf30b8d2b0..bcea28e521d 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 038e0c96b1c..1ee7b49e206 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515850","messageId":"20250408-pks-split-object-file-v1-3-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 3/9] object-file: move `xmmap()` into \"wrapper.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:11Z","receivedAt":"2025-04-08T10:24:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `xmmap()` function is provided by \"object-file.c\" even though its\nfunctionality got nothing to do with the object file subsystem. Move it\ninto \"wrapper.c\", whose header already declares those functions.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-file.c | 48 ------------------------------------------------\n wrapper.c     | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 48 insertions(+), 48 deletions(-)\n\ndiff --git a/object-file.c b/object-file.c\nindex 1a20c7fa072..ea2ed7628e6 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -719,54 +719,6 @@ int has_loose_object(const struct object_id *oid)\n \treturn check_and_freshen(oid, 0);\n }\n \n-static void mmap_limit_check(size_t length)\n-{\n-\tstatic size_t limit = 0;\n-\tif (!limit) {\n-\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n-\t\tif (!limit)\n-\t\t\tlimit = SIZE_MAX;\n-\t}\n-\tif (length > limit)\n-\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n-\t\t    (uintmax_t)length, (uintmax_t)limit);\n-}\n-\n-void *xmmap_gently(void *start, size_t length,\n-\t\t  int prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret;\n-\n-\tmmap_limit_check(length);\n-\tret = mmap(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED && !length)\n-\t\tret = NULL;\n-\treturn ret;\n-}\n-\n-const char *mmap_os_err(void)\n-{\n-\tstatic const char blank[] = \"\";\n-#if defined(__linux__)\n-\tif (errno == ENOMEM) {\n-\t\t/* this continues an existing error message: */\n-\t\tstatic const char enomem[] =\n-\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n-\t\treturn enomem;\n-\t}\n-#endif /* OS-specific bits */\n-\treturn blank;\n-}\n-\n-void *xmmap(void *start, size_t length,\n-\tint prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED)\n-\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n-\treturn ret;\n-}\n-\n static int format_object_header_literally(char *str, size_t size,\n \t\t\t\t\t  const char *type, size_t objsize)\n {\ndiff --git a/wrapper.c b/wrapper.c\nindex 8b985931490..3c79778055e 100644\n--- a/wrapper.c\n+++ b/wrapper.c\n@@ -829,3 +829,51 @@ uint32_t git_rand(unsigned flags)\n \n \treturn result;\n }\n+\n+static void mmap_limit_check(size_t length)\n+{\n+\tstatic size_t limit = 0;\n+\tif (!limit) {\n+\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n+\t\tif (!limit)\n+\t\t\tlimit = SIZE_MAX;\n+\t}\n+\tif (length > limit)\n+\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n+\t\t    (uintmax_t)length, (uintmax_t)limit);\n+}\n+\n+void *xmmap_gently(void *start, size_t length,\n+\t\t  int prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret;\n+\n+\tmmap_limit_check(length);\n+\tret = mmap(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED && !length)\n+\t\tret = NULL;\n+\treturn ret;\n+}\n+\n+const char *mmap_os_err(void)\n+{\n+\tstatic const char blank[] = \"\";\n+#if defined(__linux__)\n+\tif (errno == ENOMEM) {\n+\t\t/* this continues an existing error message: */\n+\t\tstatic const char enomem[] =\n+\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n+\t\treturn enomem;\n+\t}\n+#endif /* OS-specific bits */\n+\treturn blank;\n+}\n+\n+void *xmmap(void *start, size_t length,\n+\tint prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED)\n+\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n+\treturn ret;\n+}\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515856","messageId":"20250408-pks-split-object-file-v1-4-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 4/9] object-file: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:12Z","receivedAt":"2025-04-08T10:24:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"While we have the \"object-store.h\" header, most of the functionality for\nobject stores is actually hosted in \"object-file.c\". This makes it hard\nto find relevant functions and causes us to mix up concerns.\n\nSplit out functions relating to the object store subsystem into a new\n\"object-store.c\" file.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile                 |   1 +\n builtin/checkout.c       |   1 +\n builtin/merge-file.c     |   1 +\n builtin/mktree.c         |   1 +\n builtin/notes.c          |   1 +\n builtin/receive-pack.c   |   1 +\n builtin/tag.c            |   1 +\n builtin/unpack-file.c    |   1 +\n builtin/unpack-objects.c |   1 +\n commit.c                 |   1 +\n http-push.c              |   1 +\n match-trees.c            |   3 +-\n merge-ort.c              |   3 +-\n meson.build              |   1 +\n notes-cache.c            |   3 +-\n notes.c                  |   3 +-\n object-file.c            | 991 +----------------------------------------------\n object-file.h            |  68 +++-\n object-store-ll.h        |  60 +--\n object-store.c           | 972 ++++++++++++++++++++++++++++++++++++++++++++++\n 20 files changed, 1074 insertions(+), 1041 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex bb5407b4703..d8ad76fb791 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1086,6 +1086,7 @@ LIB_OBJS += notes.o\n LIB_OBJS += object-file-convert.o\n LIB_OBJS += object-file.o\n LIB_OBJS += object-name.o\n+LIB_OBJS += object-store.o\n LIB_OBJS += object.o\n LIB_OBJS += oid-array.o\n LIB_OBJS += oidmap.o\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e7486cf658..3e68623838a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -18,6 +18,7 @@\n #include \"lockfile.h\"\n #include \"mem-pool.h\"\n #include \"merge-recursive.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 7e315f374b2..2b16b10d2ca 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -5,6 +5,7 @@\n #include \"abspath.h\"\n #include \"diff.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store.h\"\n #include \"config.h\"\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 3c16faa40e3..0644f951161 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -11,6 +11,7 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"parse-options.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n \n static struct treeent {\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex ff61ec5f2da..0dbc233752d 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -14,6 +14,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex b3e2a9d0c60..ee51bd76f60 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -31,6 +31,7 @@\n #include \"tmp-objdir.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7c173535cb3..536a01ff3ae 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex fb5fcbc40a8..b19e5cabd03 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -2,6 +2,7 @@\n #include \"builtin.h\"\n #include \"config.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 3bbcaf2de9b..4078eab9252 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -8,6 +8,7 @@\n #include \"gettext.h\"\n #include \"git-zlib.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"object.h\"\n #include \"delta.h\"\ndiff --git a/commit.c b/commit.c\nindex 48aeefaad31..fbf4f8e87fd 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -29,6 +29,7 @@\n #include \"tree.h\"\n #include \"hook.h\"\n #include \"parse.h\"\n+#include \"object-file.h\"\n #include \"object-file-convert.h\"\n \n static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);\ndiff --git a/http-push.c b/http-push.c\nindex 1b030d96f48..806eb67cf1b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -19,6 +19,7 @@\n #include \"tree-walk.h\"\n #include \"url.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"commit-reach.h\"\n \ndiff --git a/match-trees.c b/match-trees.c\nindex ef14ceb594c..72922d5d64e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -6,7 +6,8 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n static int score_missing(unsigned mode)\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 1554900d984..5de3e2fc159 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -36,8 +36,9 @@\n #include \"merge-ll.h\"\n #include \"match-trees.h\"\n #include \"mem-pool.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"promisor-remote.h\"\ndiff --git a/meson.build b/meson.build\nindex a55e800b85b..f656f978020 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -355,6 +355,7 @@ libgit_sources = [\n   'object-file-convert.c',\n   'object-file.c',\n   'object-name.c',\n+  'object-store.c',\n   'object.c',\n   'oid-array.c',\n   'oidmap.c',\ndiff --git a/notes-cache.c b/notes-cache.c\nindex ecfdf6e43b5..150241b15e0 100644\n--- a/notes-cache.c\n+++ b/notes-cache.c\n@@ -2,7 +2,8 @@\n \n #include \"git-compat-util.h\"\n #include \"notes-cache.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"repository.h\"\n #include \"commit.h\"\ndiff --git a/notes.c b/notes.c\nindex ce5a1006a83..d9645c4b5dc 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -6,8 +6,9 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\ndiff --git a/object-file.c b/object-file.c\nindex ea2ed7628e6..1c74aa239fb 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -11,75 +11,26 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"abspath.h\"\n-#include \"config.h\"\n+#include \"bulk-checkin.h\"\n #include \"convert.h\"\n #include \"environment.h\"\n+#include \"fsck.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"string-list.h\"\n-#include \"lockfile.h\"\n-#include \"pack.h\"\n-#include \"commit.h\"\n-#include \"run-command.h\"\n-#include \"refs.h\"\n-#include \"bulk-checkin.h\"\n-#include \"repository.h\"\n-#include \"replace-object.h\"\n-#include \"streaming.h\"\n-#include \"dir.h\"\n-#include \"list.h\"\n-#include \"quote.h\"\n-#include \"packfile.h\"\n+#include \"loose.h\"\n+#include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n #include \"oidtree.h\"\n+#include \"pack.h\"\n+#include \"packfile.h\"\n #include \"path.h\"\n-#include \"promisor-remote.h\"\n #include \"setup.h\"\n-#include \"submodule.h\"\n-#include \"fsck.h\"\n-#include \"loose.h\"\n-#include \"object-file-convert.h\"\n+#include \"streaming.h\"\n \n /* The maximum size for an object header. */\n #define MAX_HEADER_LEN 32\n \n-/*\n- * This is meant to hold a *small* number of objects that you would\n- * want repo_read_object_file() to be able to return, but yet you do not want\n- * to write them into the object store (e.g. a browse-only\n- * application).\n- */\n-static struct cached_object_entry {\n-\tstruct object_id oid;\n-\tstruct cached_object {\n-\t\tenum object_type type;\n-\t\tconst void *buf;\n-\t\tunsigned long size;\n-\t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n-\n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n-{\n-\tstatic const struct cached_object empty_tree = {\n-\t\t.type = OBJ_TREE,\n-\t\t.buf = \"\",\n-\t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n-\n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n-\t\tif (oideq(&co->oid, oid))\n-\t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n-\t\treturn &empty_tree;\n-\treturn NULL;\n-}\n-\n-\n static int get_conv_flags(unsigned flags)\n {\n \tif (flags & HASH_RENORMALIZE)\n@@ -90,40 +41,6 @@ static int get_conv_flags(unsigned flags)\n \t\treturn 0;\n }\n \n-\n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n-{\n-\tint fd;\n-\t/*\n-\t * we let the umask do its job, don't try to be more\n-\t * restrictive except to remove write permission.\n-\t */\n-\tint mode = 0444;\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\t/* some mkstemp implementations erase temp_filename on failure */\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tsafe_create_leading_directories(temp_filename->buf);\n-\treturn xmkstemp_mode(temp_filename->buf, mode);\n-}\n-\n-int odb_pack_keep(const char *name)\n-{\n-\tint fd;\n-\n-\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\tsafe_create_leading_directories_const(name);\n-\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-}\n-\n static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n {\n \tint i;\n@@ -137,9 +54,9 @@ static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n \t}\n }\n \n-static const char *odb_loose_path(struct object_directory *odb,\n-\t\t\t\t  struct strbuf *buf,\n-\t\t\t\t  const struct object_id *oid)\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid)\n {\n \tstrbuf_reset(buf);\n \tstrbuf_addstr(buf, odb->path);\n@@ -148,513 +65,6 @@ static const char *odb_loose_path(struct object_directory *odb,\n \treturn buf->buf;\n }\n \n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid)\n-{\n-\treturn odb_loose_path(r->objects->odb, buf, oid);\n-}\n-\n-/*\n- * Return non-zero iff the path is usable as an alternate object database.\n- */\n-static int alt_odb_usable(struct raw_object_store *o,\n-\t\t\t  struct strbuf *path,\n-\t\t\t  const char *normalized_objdir, khiter_t *pos)\n-{\n-\tint r;\n-\n-\t/* Detect cases where alternate disappeared */\n-\tif (!is_directory(path->buf)) {\n-\t\terror(_(\"object directory %s does not exist; \"\n-\t\t\t\"check .git/objects/info/alternates\"),\n-\t\t      path->buf);\n-\t\treturn 0;\n-\t}\n-\n-\t/*\n-\t * Prevent the common mistake of listing the same\n-\t * thing twice, or object directory itself.\n-\t */\n-\tif (!o->odb_by_path) {\n-\t\tkhiter_t p;\n-\n-\t\to->odb_by_path = kh_init_odb_path_map();\n-\t\tassert(!o->odb->next);\n-\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n-\t\tassert(r == 1); /* never used */\n-\t\tkh_value(o->odb_by_path, p) = o->odb;\n-\t}\n-\tif (fspatheq(path->buf, normalized_objdir))\n-\t\treturn 0;\n-\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n-\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n-\treturn r == 0 ? 0 : 1;\n-}\n-\n-/*\n- * Prepare alternate object database registry.\n- *\n- * The variable alt_odb_list points at the list of struct\n- * object_directory.  The elements on this list come from\n- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n- * whose contents is similar to that environment variable but can be\n- * LF separated.  Its base points at a statically allocated buffer that\n- * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n- * its name points just after the slash at the end of \".git/objects/\"\n- * in the example above, and has enough space to hold all hex characters\n- * of the object ID, an extra slash for the first level indirection, and\n- * the terminating NUL.\n- */\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth);\n-static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n-\tconst char *relative_base, int depth, const char *normalized_objdir)\n-{\n-\tstruct object_directory *ent;\n-\tstruct strbuf pathbuf = STRBUF_INIT;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\tkhiter_t pos;\n-\tint ret = -1;\n-\n-\tif (!is_absolute_path(entry->buf) && relative_base) {\n-\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n-\t\tstrbuf_addch(&pathbuf, '/');\n-\t}\n-\tstrbuf_addbuf(&pathbuf, entry);\n-\n-\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n-\t\terror(_(\"unable to normalize alternate object path: %s\"),\n-\t\t      pathbuf.buf);\n-\t\tgoto error;\n-\t}\n-\tstrbuf_swap(&pathbuf, &tmp);\n-\n-\t/*\n-\t * The trailing slash after the directory name is given by\n-\t * this function at the end. Remove duplicates.\n-\t */\n-\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n-\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n-\n-\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n-\t\tgoto error;\n-\n-\tCALLOC_ARRAY(ent, 1);\n-\t/* pathbuf.buf is already in r->objects->odb_by_path */\n-\tent->path = strbuf_detach(&pathbuf, NULL);\n-\n-\t/* add the alternate entry */\n-\t*r->objects->odb_tail = ent;\n-\tr->objects->odb_tail = &(ent->next);\n-\tent->next = NULL;\n-\tassert(r->objects->odb_by_path);\n-\tkh_value(r->objects->odb_by_path, pos) = ent;\n-\n-\t/* recursively add alternates */\n-\tread_info_alternates(r, ent->path, depth + 1);\n-\tret = 0;\n- error:\n-\tstrbuf_release(&tmp);\n-\tstrbuf_release(&pathbuf);\n-\treturn ret;\n-}\n-\n-static const char *parse_alt_odb_entry(const char *string,\n-\t\t\t\t       int sep,\n-\t\t\t\t       struct strbuf *out)\n-{\n-\tconst char *end;\n-\n-\tstrbuf_reset(out);\n-\n-\tif (*string == '#') {\n-\t\t/* comment; consume up to next separator */\n-\t\tend = strchrnul(string, sep);\n-\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n-\t\t/*\n-\t\t * quoted path; unquote_c_style has copied the\n-\t\t * data for us and set \"end\". Broken quoting (e.g.,\n-\t\t * an entry that doesn't end with a quote) falls\n-\t\t * back to the unquoted case below.\n-\t\t */\n-\t} else {\n-\t\t/* normal, unquoted path */\n-\t\tend = strchrnul(string, sep);\n-\t\tstrbuf_add(out, string, end - string);\n-\t}\n-\n-\tif (*end)\n-\t\tend++;\n-\treturn end;\n-}\n-\n-static void link_alt_odb_entries(struct repository *r, const char *alt,\n-\t\t\t\t int sep, const char *relative_base, int depth)\n-{\n-\tstruct strbuf objdirbuf = STRBUF_INIT;\n-\tstruct strbuf entry = STRBUF_INIT;\n-\n-\tif (!alt || !*alt)\n-\t\treturn;\n-\n-\tif (depth > 5) {\n-\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n-\t\t\t\trelative_base);\n-\t\treturn;\n-\t}\n-\n-\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n-\n-\twhile (*alt) {\n-\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n-\t\tif (!entry.len)\n-\t\t\tcontinue;\n-\t\tlink_alt_odb_entry(r, &entry,\n-\t\t\t\t   relative_base, depth, objdirbuf.buf);\n-\t}\n-\tstrbuf_release(&entry);\n-\tstrbuf_release(&objdirbuf);\n-}\n-\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth)\n-{\n-\tchar *path;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n-\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n-\t\twarn_on_fopen_errors(path);\n-\t\tfree(path);\n-\t\treturn;\n-\t}\n-\n-\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n-\tstrbuf_release(&buf);\n-\tfree(path);\n-}\n-\n-void add_to_alternates_file(const char *reference)\n-{\n-\tstruct lock_file lock = LOCK_INIT;\n-\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n-\tFILE *in, *out;\n-\tint found = 0;\n-\n-\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n-\tout = fdopen_lock_file(&lock, \"w\");\n-\tif (!out)\n-\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n-\n-\tin = fopen(alts, \"r\");\n-\tif (in) {\n-\t\tstruct strbuf line = STRBUF_INIT;\n-\n-\t\twhile (strbuf_getline(&line, in) != EOF) {\n-\t\t\tif (!strcmp(reference, line.buf)) {\n-\t\t\t\tfound = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n-\t\t}\n-\n-\t\tstrbuf_release(&line);\n-\t\tfclose(in);\n-\t}\n-\telse if (errno != ENOENT)\n-\t\tdie_errno(_(\"unable to read alternates file\"));\n-\n-\tif (found) {\n-\t\trollback_lock_file(&lock);\n-\t} else {\n-\t\tfprintf_or_die(out, \"%s\\n\", reference);\n-\t\tif (commit_lock_file(&lock))\n-\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n-\t\tif (the_repository->objects->loaded_alternates)\n-\t\t\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t\t\t     '\\n', NULL, 0);\n-\t}\n-\tfree(alts);\n-}\n-\n-void add_to_alternates_memory(const char *reference)\n-{\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t     '\\n', NULL, 0);\n-}\n-\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n-{\n-\tstruct object_directory *new_odb;\n-\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\t/*\n-\t * Make a new primary odb and link the old primary ODB in as an\n-\t * alternate\n-\t */\n-\tnew_odb = xcalloc(1, sizeof(*new_odb));\n-\tnew_odb->path = xstrdup(dir);\n-\n-\t/*\n-\t * Disable ref updates while a temporary odb is active, since\n-\t * the objects in the database may roll back.\n-\t */\n-\tnew_odb->disable_ref_updates = 1;\n-\tnew_odb->will_destroy = will_destroy;\n-\tnew_odb->next = the_repository->objects->odb;\n-\tthe_repository->objects->odb = new_odb;\n-\treturn new_odb->next;\n-}\n-\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n-{\n-\tstruct object_directory *cur_odb = the_repository->objects->odb;\n-\n-\tif (strcmp(old_path, cur_odb->path))\n-\t\tBUG(\"expected %s as primary object store; found %s\",\n-\t\t    old_path, cur_odb->path);\n-\n-\tif (cur_odb->next != restore_odb)\n-\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n-\n-\tthe_repository->objects->odb = restore_odb;\n-\tfree_object_directory(cur_odb);\n-}\n-\n-/*\n- * Compute the exact path an alternate is at and returns it. In case of\n- * error NULL is returned and the human readable error is added to `err`\n- * `path` may be relative and should point to $GIT_DIR.\n- * `err` must not be null.\n- */\n-char *compute_alternate_path(const char *path, struct strbuf *err)\n-{\n-\tchar *ref_git = NULL;\n-\tconst char *repo;\n-\tint seen_error = 0;\n-\n-\tref_git = real_pathdup(path, 0);\n-\tif (!ref_git) {\n-\t\tseen_error = 1;\n-\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\trepo = read_gitfile(ref_git);\n-\tif (!repo)\n-\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n-\tif (repo) {\n-\t\tfree(ref_git);\n-\t\tref_git = xstrdup(repo);\n-\t}\n-\n-\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n-\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n-\t\tfree(ref_git);\n-\t\tref_git = ref_git_git;\n-\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tseen_error = 1;\n-\t\tif (get_common_dir(&sb, ref_git)) {\n-\t\t\tstrbuf_addf(err,\n-\t\t\t\t    _(\"reference repository '%s' as a linked \"\n-\t\t\t\t      \"checkout is not supported yet.\"),\n-\t\t\t\t    path);\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n-\t\t\t\t\t\"local repository.\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err,\n-\t\t\t    _(\"reference repository '%s' is grafted\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-out:\n-\tif (seen_error) {\n-\t\tFREE_AND_NULL(ref_git);\n-\t}\n-\n-\treturn ref_git;\n-}\n-\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n-{\n-\tstruct object_directory *odb;\n-\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n-\tstruct strbuf odb_path_real = STRBUF_INIT;\n-\n-\tprepare_alt_odb(r);\n-\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n-\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n-\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n-\t\t\tbreak;\n-\t}\n-\n-\tfree(obj_dir_real);\n-\tstrbuf_release(&odb_path_real);\n-\n-\tif (!odb)\n-\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n-\treturn odb;\n-}\n-\n-static void fill_alternate_refs_command(struct child_process *cmd,\n-\t\t\t\t\tconst char *repo_path)\n-{\n-\tconst char *value;\n-\n-\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n-\t\tcmd->use_shell = 1;\n-\n-\t\tstrvec_push(&cmd->args, value);\n-\t\tstrvec_push(&cmd->args, repo_path);\n-\t} else {\n-\t\tcmd->git_cmd = 1;\n-\n-\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n-\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n-\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n-\n-\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n-\t\t\tstrvec_push(&cmd->args, \"--\");\n-\t\t\tstrvec_split(&cmd->args, value);\n-\t\t}\n-\t}\n-\n-\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n-\tcmd->out = -1;\n-}\n-\n-static void read_alternate_refs(const char *path,\n-\t\t\t\talternate_ref_fn *cb,\n-\t\t\t\tvoid *data)\n-{\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tstruct strbuf line = STRBUF_INIT;\n-\tFILE *fh;\n-\n-\tfill_alternate_refs_command(&cmd, path);\n-\n-\tif (start_command(&cmd))\n-\t\treturn;\n-\n-\tfh = xfdopen(cmd.out, \"r\");\n-\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n-\t\tstruct object_id oid;\n-\t\tconst char *p;\n-\n-\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n-\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n-\t\t\t\tline.buf);\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tcb(&oid, data);\n-\t}\n-\n-\tfclose(fh);\n-\tfinish_command(&cmd);\n-\tstrbuf_release(&line);\n-}\n-\n-struct alternate_refs_data {\n-\talternate_ref_fn *fn;\n-\tvoid *data;\n-};\n-\n-static int refs_from_alternate_cb(struct object_directory *e,\n-\t\t\t\t  void *data)\n-{\n-\tstruct strbuf path = STRBUF_INIT;\n-\tsize_t base_len;\n-\tstruct alternate_refs_data *cb = data;\n-\n-\tif (!strbuf_realpath(&path, e->path, 0))\n-\t\tgoto out;\n-\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n-\t\tgoto out;\n-\tbase_len = path.len;\n-\n-\t/* Is this a git repository with refs? */\n-\tstrbuf_addstr(&path, \"/refs\");\n-\tif (!is_directory(path.buf))\n-\t\tgoto out;\n-\tstrbuf_setlen(&path, base_len);\n-\n-\tread_alternate_refs(path.buf, cb->fn, cb->data);\n-\n-out:\n-\tstrbuf_release(&path);\n-\treturn 0;\n-}\n-\n-void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n-{\n-\tstruct alternate_refs_data cb;\n-\tcb.fn = fn;\n-\tcb.data = data;\n-\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n-}\n-\n-int foreach_alt_odb(alt_odb_fn fn, void *cb)\n-{\n-\tstruct object_directory *ent;\n-\tint r = 0;\n-\n-\tprepare_alt_odb(the_repository);\n-\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n-\t\tr = fn(ent, cb);\n-\t\tif (r)\n-\t\t\tbreak;\n-\t}\n-\treturn r;\n-}\n-\n-void prepare_alt_odb(struct repository *r)\n-{\n-\tif (r->objects->loaded_alternates)\n-\t\treturn;\n-\n-\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n-\n-\tread_info_alternates(r, r->objects->odb->path, 0);\n-\tr->objects->loaded_alternates = 1;\n-}\n-\n-int has_alt_odb(struct repository *r)\n-{\n-\tprepare_alt_odb(r);\n-\treturn !!r->objects->odb->next;\n-}\n-\n /* Returns 1 if we have successfully freshened the file, 0 otherwise. */\n static int freshen_file(const char *fn)\n {\n@@ -1056,9 +466,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)\n \treturn 0;\n }\n \n-static int loose_object_info(struct repository *r,\n-\t\t\t     const struct object_id *oid,\n-\t\t\t     struct object_info *oi, int flags)\n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags)\n {\n \tint status = 0;\n \tint fd;\n@@ -1154,345 +564,6 @@ static int loose_object_info(struct repository *r,\n \treturn status;\n }\n \n-int obj_read_use_lock = 0;\n-pthread_mutex_t obj_read_mutex;\n-\n-void enable_obj_read_lock(void)\n-{\n-\tif (obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 1;\n-\tinit_recursive_mutex(&obj_read_mutex);\n-}\n-\n-void disable_obj_read_lock(void)\n-{\n-\tif (!obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 0;\n-\tpthread_mutex_destroy(&obj_read_mutex);\n-}\n-\n-int fetch_if_missing = 1;\n-\n-static int do_oid_object_info_extended(struct repository *r,\n-\t\t\t\t       const struct object_id *oid,\n-\t\t\t\t       struct object_info *oi, unsigned flags)\n-{\n-\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n-\tconst struct cached_object *co;\n-\tstruct pack_entry e;\n-\tint rtype;\n-\tconst struct object_id *real = oid;\n-\tint already_retried = 0;\n-\n-\n-\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n-\t\treal = lookup_replace_object(r, oid);\n-\n-\tif (is_null_oid(real))\n-\t\treturn -1;\n-\n-\tif (!oi)\n-\t\toi = &blank_oi;\n-\n-\tco = find_cached_object(real);\n-\tif (co) {\n-\t\tif (oi->typep)\n-\t\t\t*(oi->typep) = co->type;\n-\t\tif (oi->sizep)\n-\t\t\t*(oi->sizep) = co->size;\n-\t\tif (oi->disk_sizep)\n-\t\t\t*(oi->disk_sizep) = 0;\n-\t\tif (oi->delta_base_oid)\n-\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n-\t\tif (oi->type_name)\n-\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n-\t\tif (oi->contentp)\n-\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n-\t\toi->whence = OI_CACHED;\n-\t\treturn 0;\n-\t}\n-\n-\twhile (1) {\n-\t\tif (find_pack_entry(r, real, &e))\n-\t\t\tbreak;\n-\n-\t\t/* Most likely it's a loose object. */\n-\t\tif (!loose_object_info(r, real, oi, flags))\n-\t\t\treturn 0;\n-\n-\t\t/* Not a loose object; someone else may have just packed it. */\n-\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n-\t\t\treprepare_packed_git(r);\n-\t\t\tif (find_pack_entry(r, real, &e))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\t/*\n-\t\t * If r is the_repository, this might be an attempt at\n-\t\t * accessing a submodule object as if it were in the_repository\n-\t\t * (having called add_submodule_odb() on that submodule's ODB).\n-\t\t * If any such ODBs exist, register them and try again.\n-\t\t */\n-\t\tif (r == the_repository &&\n-\t\t    register_all_submodule_odb_as_alternates())\n-\t\t\t/* We added some alternates; retry */\n-\t\t\tcontinue;\n-\n-\t\t/* Check if it is a missing object */\n-\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n-\t\t    !already_retried &&\n-\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n-\t\t\tpromisor_remote_get_direct(r, real, 1);\n-\t\t\talready_retried = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n-\t\t\tconst struct packed_git *p;\n-\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n-\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n-\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n-\t\t\tif ((p = has_packed_and_bad(r, real)))\n-\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n-\t\t\t\t    oid_to_hex(real), p->pack_name);\n-\t\t}\n-\t\treturn -1;\n-\t}\n-\n-\tif (oi == &blank_oi)\n-\t\t/*\n-\t\t * We know that the caller doesn't actually need the\n-\t\t * information below, so return early.\n-\t\t */\n-\t\treturn 0;\n-\trtype = packed_object_info(r, e.p, e.offset, oi);\n-\tif (rtype < 0) {\n-\t\tmark_bad_packed_object(e.p, real);\n-\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n-\t} else if (oi->whence == OI_PACKED) {\n-\t\toi->u.packed.offset = e.offset;\n-\t\toi->u.packed.pack = e.p;\n-\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n-\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int oid_object_info_convert(struct repository *r,\n-\t\t\t\t   const struct object_id *input_oid,\n-\t\t\t\t   struct object_info *input_oi, unsigned flags)\n-{\n-\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n-\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n-\tstruct strbuf type_name = STRBUF_INIT;\n-\tstruct object_id oid, delta_base_oid;\n-\tstruct object_info new_oi, *oi;\n-\tunsigned long size;\n-\tvoid *content;\n-\tint ret;\n-\n-\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n-\t\tif (do_die)\n-\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n-\t\treturn -1;\n-\t}\n-\n-\t/* Is new_oi needed? */\n-\toi = input_oi;\n-\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n-\t\t\t input_oi->contentp)) {\n-\t\tnew_oi = *input_oi;\n-\t\t/* Does delta_base_oid need to be converted? */\n-\t\tif (input_oi->delta_base_oid)\n-\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n-\t\t/* Will the attributes differ when converted? */\n-\t\tif (input_oi->sizep || input_oi->contentp) {\n-\t\t\tnew_oi.contentp = &content;\n-\t\t\tnew_oi.sizep = &size;\n-\t\t\tnew_oi.type_name = &type_name;\n-\t\t}\n-\t\toi = &new_oi;\n-\t}\n-\n-\tret = oid_object_info_extended(r, &oid, oi, flags);\n-\tif (ret)\n-\t\treturn -1;\n-\tif (oi == input_oi)\n-\t\treturn ret;\n-\n-\tif (new_oi.contentp) {\n-\t\tstruct strbuf outbuf = STRBUF_INIT;\n-\t\tenum object_type type;\n-\n-\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n-\t\t\t\t\t       !do_die);\n-\t\tif (type == -1)\n-\t\t\treturn -1;\n-\t\tif (type != OBJ_BLOB) {\n-\t\t\tret = convert_object_file(the_repository, &outbuf,\n-\t\t\t\t\t\t  the_hash_algo, input_algo,\n-\t\t\t\t\t\t  content, size, type, !do_die);\n-\t\t\tfree(content);\n-\t\t\tif (ret == -1)\n-\t\t\t\treturn -1;\n-\t\t\tsize = outbuf.len;\n-\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n-\t\t}\n-\t\tif (input_oi->sizep)\n-\t\t\t*input_oi->sizep = size;\n-\t\tif (input_oi->contentp)\n-\t\t\t*input_oi->contentp = content;\n-\t\telse\n-\t\t\tfree(content);\n-\t\tif (input_oi->type_name)\n-\t\t\t*input_oi->type_name = type_name;\n-\t\telse\n-\t\t\tstrbuf_release(&type_name);\n-\t}\n-\tif (new_oi.delta_base_oid == &delta_base_oid) {\n-\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n-\t\t\t\t input_oi->delta_base_oid)) {\n-\t\t\tif (do_die)\n-\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t\t    oid_to_hex(&delta_base_oid),\n-\t\t\t\t    input_algo->name);\n-\t\t\treturn -1;\n-\t\t}\n-\t}\n-\tinput_oi->whence = new_oi.whence;\n-\tinput_oi->u = new_oi.u;\n-\treturn ret;\n-}\n-\n-int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n-\t\t\t     struct object_info *oi, unsigned flags)\n-{\n-\tint ret;\n-\n-\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n-\t\treturn oid_object_info_convert(r, oid, oi, flags);\n-\n-\tobj_read_lock();\n-\tret = do_oid_object_info_extended(r, oid, oi, flags);\n-\tobj_read_unlock();\n-\treturn ret;\n-}\n-\n-\n-/* returns enum object_type or negative */\n-int oid_object_info(struct repository *r,\n-\t\t    const struct object_id *oid,\n-\t\t    unsigned long *sizep)\n-{\n-\tenum object_type type;\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\n-\toi.typep = &type;\n-\toi.sizep = sizep;\n-\tif (oid_object_info_extended(r, oid, &oi,\n-\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n-\t\treturn -1;\n-\treturn type;\n-}\n-\n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid)\n-{\n-\tstruct cached_object_entry *co;\n-\tchar *co_buf;\n-\n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n-\t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n-\tco->value.size = len;\n-\tco->value.type = type;\n-\tco_buf = xmalloc(len);\n-\tmemcpy(co_buf, buf, len);\n-\tco->value.buf = co_buf;\n-\toidcpy(&co->oid, oid);\n-\treturn 0;\n-}\n-\n-/*\n- * This function dies on corrupt objects; the callers who want to\n- * deal with them should arrange to call oid_object_info_extended() and give\n- * error messages themselves.\n- */\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size)\n-{\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n-\tvoid *data;\n-\n-\toi.typep = type;\n-\toi.sizep = size;\n-\toi.contentp = &data;\n-\tif (oid_object_info_extended(r, oid, &oi, flags))\n-\t\treturn NULL;\n-\n-\treturn data;\n-}\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *actual_oid_return)\n-{\n-\tenum object_type type;\n-\tvoid *buffer;\n-\tunsigned long isize;\n-\tstruct object_id actual_oid;\n-\n-\toidcpy(&actual_oid, oid);\n-\twhile (1) {\n-\t\tint ref_length = -1;\n-\t\tconst char *ref_type = NULL;\n-\n-\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n-\t\tif (!buffer)\n-\t\t\treturn NULL;\n-\t\tif (type == required_type) {\n-\t\t\t*size = isize;\n-\t\t\tif (actual_oid_return)\n-\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n-\t\t\treturn buffer;\n-\t\t}\n-\t\t/* Handle references */\n-\t\telse if (type == OBJ_COMMIT)\n-\t\t\tref_type = \"tree \";\n-\t\telse if (type == OBJ_TAG)\n-\t\t\tref_type = \"object \";\n-\t\telse {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tref_length = strlen(ref_type);\n-\n-\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n-\t\t    memcmp(buffer, ref_type, ref_length) ||\n-\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tfree(buffer);\n-\t\t/* Now we have the ID of the referred-to object in\n-\t\t * actual_oid.  Check again. */\n-\t}\n-}\n-\n static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,\n \t\t\t     const void *buf, unsigned long len,\n \t\t\t     struct object_id *oid,\n@@ -2154,32 +1225,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)\n \treturn ret;\n }\n \n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags)\n-{\n-\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n-\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n-\t\t(quick ? OBJECT_INFO_QUICK : 0);\n-\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n-}\n-\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags)\n-{\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n-}\n-\n-int repo_has_object_file(struct repository *r,\n-\t\t\t const struct object_id *oid)\n-{\n-\treturn repo_has_object_file_with_flags(r, oid, 0);\n-}\n-\n /*\n  * We can't use the normal fsck_error_function() for index_mem(),\n  * because we don't yet have a valid oid for it to report. Instead,\n@@ -2407,16 +1452,6 @@ int read_pack_header(int fd, struct pack_header *header)\n \treturn 0;\n }\n \n-void assert_oid_type(const struct object_id *oid, enum object_type expect)\n-{\n-\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n-\tif (type < 0)\n-\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n-\tif (type != expect)\n-\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n-\t\t    type_name(expect));\n-}\n-\n int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n \t\t\t\tstruct strbuf *path,\n \t\t\t\teach_loose_object_fn obj_cb,\ndiff --git a/object-file.h b/object-file.h\nindex 353d8a85c33..78c84d970a9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,6 +21,29 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n+struct object_directory;\n+\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid);\n+\n+/*\n+ * Return true iff an alternate object database has a loose object\n+ * with the specified name.  This function does not respect replace\n+ * references.\n+ */\n+int has_loose_object_nonlocal(const struct object_id *);\n+\n+int has_loose_object(const struct object_id *);\n+\n+/**\n+ * format_object_header() is a thin wrapper around s xsnprintf() that\n+ * writes the initial \"<type> <obj-len>\" part of the loose object\n+ * header. It returns the size that snprintf() returns + 1.\n+ */\n+int format_object_header(char *str, size_t size, enum object_type type,\n+\t\t\t size_t objsize);\n+\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\n@@ -61,6 +84,29 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+int write_object_file_flags(const void *buf, unsigned long len,\n+\t\t\t    enum object_type type, struct object_id *oid,\n+\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n+static inline int write_object_file(const void *buf, unsigned long len,\n+\t\t\t\t    enum object_type type, struct object_id *oid)\n+{\n+\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n+}\n+\n+struct input_stream {\n+\tconst void *(*read)(struct input_stream *, unsigned long *len);\n+\tvoid *data;\n+\tint is_finished;\n+};\n+\n+int write_object_file_literally(const void *buf, unsigned long len,\n+\t\t\t\tconst char *type, struct object_id *oid,\n+\t\t\t\tunsigned flags);\n+int stream_loose_object(struct input_stream *in_stream, size_t len,\n+\t\t\tstruct object_id *oid);\n+\n+int force_object_loose(const struct object_id *oid, time_t mtime);\n+\n /**\n  * With in-core object data in \"buf\", rehash it to make sure the\n  * object name actually matches \"oid\" to detect object corruption.\n@@ -79,6 +125,10 @@ int check_object_signature(struct repository *r, const struct object_id *oid,\n  */\n int stream_object_signature(struct repository *r, const struct object_id *oid);\n \n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags);\n+\n enum finalize_object_file_flags {\n \tFOF_SKIP_COLLISION_CHECK = 1,\n };\n@@ -90,10 +140,18 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,\n /* Helper to check and \"touch\" a file */\n int check_and_freshen_file(const char *fn, int freshen);\n \n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n+/*\n+ * Open the loose object at path, check its hash, and return the contents,\n+ * use the \"oi\" argument to assert things about the object, or e.g. populate its\n+ * type, and size. If the object is a blob, then \"contents\" may return NULL,\n+ * to allow streaming of large blobs.\n+ *\n+ * Returns 0 on success, negative on error (details may be written to stderr).\n+ */\n+int read_loose_object(const char *path,\n+\t\t      const struct object_id *expected_oid,\n+\t\t      struct object_id *real_oid,\n+\t\t      void **contents,\n+\t\t      struct object_info *oi);\n \n #endif /* OBJECT_FILE_H */\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex cd3bd5bd99f..8ae80b8a5fa 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -49,12 +49,6 @@ struct object_directory {\n \tchar *path;\n };\n \n-struct input_stream {\n-\tconst void *(*read)(struct input_stream *, unsigned long *len);\n-\tvoid *data;\n-\tint is_finished;\n-};\n-\n void prepare_alt_odb(struct repository *r);\n int has_alt_odb(struct repository *r);\n char *compute_alternate_path(const char *path, struct strbuf *err);\n@@ -273,21 +267,6 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n \t\t      unsigned long len, enum object_type type,\n \t\t      struct object_id *oid);\n \n-int write_object_file_flags(const void *buf, unsigned long len,\n-\t\t\t    enum object_type type, struct object_id *oid,\n-\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n-static inline int write_object_file(const void *buf, unsigned long len,\n-\t\t\t\t    enum object_type type, struct object_id *oid)\n-{\n-\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n-}\n-\n-int write_object_file_literally(const void *buf, unsigned long len,\n-\t\t\t\tconst char *type, struct object_id *oid,\n-\t\t\t\tunsigned flags);\n-int stream_loose_object(struct input_stream *in_stream, size_t len,\n-\t\t\tstruct object_id *oid);\n-\n /*\n  * Add an object file to the in-memory object store, without writing it\n  * to disk.\n@@ -299,8 +278,6 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,\n int pretend_object_file(void *, unsigned long, enum object_type,\n \t\t\tstruct object_id *oid);\n \n-int force_object_loose(const struct object_id *oid, time_t mtime);\n-\n struct object_info {\n \t/* Request */\n \tenum object_type *typep;\n@@ -364,20 +341,6 @@ int oid_object_info_extended(struct repository *r,\n \t\t\t     const struct object_id *,\n \t\t\t     struct object_info *, unsigned flags);\n \n-/*\n- * Open the loose object at path, check its hash, and return the contents,\n- * use the \"oi\" argument to assert things about the object, or e.g. populate its\n- * type, and size. If the object is a blob, then \"contents\" may return NULL,\n- * to allow streaming of large blobs.\n- *\n- * Returns 0 on success, negative on error (details may be written to stderr).\n- */\n-int read_loose_object(const char *path,\n-\t\t      const struct object_id *expected_oid,\n-\t\t      struct object_id *real_oid,\n-\t\t      void **contents,\n-\t\t      struct object_info *oi);\n-\n /* Retry packed storage after checking packed and loose storage */\n #define HAS_OBJECT_RECHECK_PACKED 1\n \n@@ -405,23 +368,6 @@ int repo_has_object_file(struct repository *r, const struct object_id *oid);\n int repo_has_object_file_with_flags(struct repository *r,\n \t\t\t\t    const struct object_id *oid, int flags);\n \n-/*\n- * Return true iff an alternate object database has a loose object\n- * with the specified name.  This function does not respect replace\n- * references.\n- */\n-int has_loose_object_nonlocal(const struct object_id *);\n-\n-int has_loose_object(const struct object_id *);\n-\n-/**\n- * format_object_header() is a thin wrapper around s xsnprintf() that\n- * writes the initial \"<type> <obj-len>\" part of the loose object\n- * header. It returns the size that snprintf() returns + 1.\n- */\n-int format_object_header(char *str, size_t size, enum object_type type,\n-\t\t\t size_t objsize);\n-\n void assert_oid_type(const struct object_id *oid, enum object_type expect);\n \n /*\n@@ -553,4 +499,10 @@ int for_each_object_in_pack(struct packed_git *p,\n int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n \t\t\t   void *data, enum for_each_object_flags flags);\n \n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n+\n #endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nnew file mode 100644\nindex 00000000000..e5f1f00cdde\n--- /dev/null\n+++ b/object-store.c\n@@ -0,0 +1,972 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"abspath.h\"\n+#include \"config.h\"\n+#include \"environment.h\"\n+#include \"gettext.h\"\n+#include \"hex.h\"\n+#include \"lockfile.h\"\n+#include \"object-file-convert.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n+#include \"packfile.h\"\n+#include \"path.h\"\n+#include \"promisor-remote.h\"\n+#include \"quote.h\"\n+#include \"replace-object.h\"\n+#include \"run-command.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"strvec.h\"\n+#include \"submodule.h\"\n+#include \"write-or-die.h\"\n+\n+/*\n+ * This is meant to hold a *small* number of objects that you would\n+ * want repo_read_object_file() to be able to return, but yet you do not want\n+ * to write them into the object store (e.g. a browse-only\n+ * application).\n+ */\n+static struct cached_object_entry {\n+\tstruct object_id oid;\n+\tstruct cached_object {\n+\t\tenum object_type type;\n+\t\tconst void *buf;\n+\t\tunsigned long size;\n+\t} value;\n+} *cached_objects;\n+static int cached_object_nr, cached_object_alloc;\n+\n+static const struct cached_object *find_cached_object(const struct object_id *oid)\n+{\n+\tstatic const struct cached_object empty_tree = {\n+\t\t.type = OBJ_TREE,\n+\t\t.buf = \"\",\n+\t};\n+\tint i;\n+\tconst struct cached_object_entry *co = cached_objects;\n+\n+\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\t\tif (oideq(&co->oid, oid))\n+\t\t\treturn &co->value;\n+\t}\n+\tif (oideq(oid, the_hash_algo->empty_tree))\n+\t\treturn &empty_tree;\n+\treturn NULL;\n+}\n+\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n+{\n+\tint fd;\n+\t/*\n+\t * we let the umask do its job, don't try to be more\n+\t * restrictive except to remove write permission.\n+\t */\n+\tint mode = 0444;\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\t/* some mkstemp implementations erase temp_filename on failure */\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tsafe_create_leading_directories(temp_filename->buf);\n+\treturn xmkstemp_mode(temp_filename->buf, mode);\n+}\n+\n+int odb_pack_keep(const char *name)\n+{\n+\tint fd;\n+\n+\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\tsafe_create_leading_directories_const(name);\n+\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+}\n+\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid)\n+{\n+\treturn odb_loose_path(r->objects->odb, buf, oid);\n+}\n+\n+/*\n+ * Return non-zero iff the path is usable as an alternate object database.\n+ */\n+static int alt_odb_usable(struct raw_object_store *o,\n+\t\t\t  struct strbuf *path,\n+\t\t\t  const char *normalized_objdir, khiter_t *pos)\n+{\n+\tint r;\n+\n+\t/* Detect cases where alternate disappeared */\n+\tif (!is_directory(path->buf)) {\n+\t\terror(_(\"object directory %s does not exist; \"\n+\t\t\t\"check .git/objects/info/alternates\"),\n+\t\t      path->buf);\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Prevent the common mistake of listing the same\n+\t * thing twice, or object directory itself.\n+\t */\n+\tif (!o->odb_by_path) {\n+\t\tkhiter_t p;\n+\n+\t\to->odb_by_path = kh_init_odb_path_map();\n+\t\tassert(!o->odb->next);\n+\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n+\t\tassert(r == 1); /* never used */\n+\t\tkh_value(o->odb_by_path, p) = o->odb;\n+\t}\n+\tif (fspatheq(path->buf, normalized_objdir))\n+\t\treturn 0;\n+\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n+\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n+\treturn r == 0 ? 0 : 1;\n+}\n+\n+/*\n+ * Prepare alternate object database registry.\n+ *\n+ * The variable alt_odb_list points at the list of struct\n+ * object_directory.  The elements on this list come from\n+ * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n+ * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n+ * whose contents is similar to that environment variable but can be\n+ * LF separated.  Its base points at a statically allocated buffer that\n+ * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n+ * its name points just after the slash at the end of \".git/objects/\"\n+ * in the example above, and has enough space to hold all hex characters\n+ * of the object ID, an extra slash for the first level indirection, and\n+ * the terminating NUL.\n+ */\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth);\n+static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n+\tconst char *relative_base, int depth, const char *normalized_objdir)\n+{\n+\tstruct object_directory *ent;\n+\tstruct strbuf pathbuf = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tkhiter_t pos;\n+\tint ret = -1;\n+\n+\tif (!is_absolute_path(entry->buf) && relative_base) {\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n+\t\tstrbuf_addch(&pathbuf, '/');\n+\t}\n+\tstrbuf_addbuf(&pathbuf, entry);\n+\n+\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n+\t\terror(_(\"unable to normalize alternate object path: %s\"),\n+\t\t      pathbuf.buf);\n+\t\tgoto error;\n+\t}\n+\tstrbuf_swap(&pathbuf, &tmp);\n+\n+\t/*\n+\t * The trailing slash after the directory name is given by\n+\t * this function at the end. Remove duplicates.\n+\t */\n+\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n+\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n+\n+\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n+\t\tgoto error;\n+\n+\tCALLOC_ARRAY(ent, 1);\n+\t/* pathbuf.buf is already in r->objects->odb_by_path */\n+\tent->path = strbuf_detach(&pathbuf, NULL);\n+\n+\t/* add the alternate entry */\n+\t*r->objects->odb_tail = ent;\n+\tr->objects->odb_tail = &(ent->next);\n+\tent->next = NULL;\n+\tassert(r->objects->odb_by_path);\n+\tkh_value(r->objects->odb_by_path, pos) = ent;\n+\n+\t/* recursively add alternates */\n+\tread_info_alternates(r, ent->path, depth + 1);\n+\tret = 0;\n+ error:\n+\tstrbuf_release(&tmp);\n+\tstrbuf_release(&pathbuf);\n+\treturn ret;\n+}\n+\n+static const char *parse_alt_odb_entry(const char *string,\n+\t\t\t\t       int sep,\n+\t\t\t\t       struct strbuf *out)\n+{\n+\tconst char *end;\n+\n+\tstrbuf_reset(out);\n+\n+\tif (*string == '#') {\n+\t\t/* comment; consume up to next separator */\n+\t\tend = strchrnul(string, sep);\n+\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n+\t\t/*\n+\t\t * quoted path; unquote_c_style has copied the\n+\t\t * data for us and set \"end\". Broken quoting (e.g.,\n+\t\t * an entry that doesn't end with a quote) falls\n+\t\t * back to the unquoted case below.\n+\t\t */\n+\t} else {\n+\t\t/* normal, unquoted path */\n+\t\tend = strchrnul(string, sep);\n+\t\tstrbuf_add(out, string, end - string);\n+\t}\n+\n+\tif (*end)\n+\t\tend++;\n+\treturn end;\n+}\n+\n+static void link_alt_odb_entries(struct repository *r, const char *alt,\n+\t\t\t\t int sep, const char *relative_base, int depth)\n+{\n+\tstruct strbuf objdirbuf = STRBUF_INIT;\n+\tstruct strbuf entry = STRBUF_INIT;\n+\n+\tif (!alt || !*alt)\n+\t\treturn;\n+\n+\tif (depth > 5) {\n+\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n+\t\t\t\trelative_base);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n+\n+\twhile (*alt) {\n+\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n+\t\tif (!entry.len)\n+\t\t\tcontinue;\n+\t\tlink_alt_odb_entry(r, &entry,\n+\t\t\t\t   relative_base, depth, objdirbuf.buf);\n+\t}\n+\tstrbuf_release(&entry);\n+\tstrbuf_release(&objdirbuf);\n+}\n+\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth)\n+{\n+\tchar *path;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n+\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n+\t\twarn_on_fopen_errors(path);\n+\t\tfree(path);\n+\t\treturn;\n+\t}\n+\n+\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n+\tstrbuf_release(&buf);\n+\tfree(path);\n+}\n+\n+void add_to_alternates_file(const char *reference)\n+{\n+\tstruct lock_file lock = LOCK_INIT;\n+\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n+\tFILE *in, *out;\n+\tint found = 0;\n+\n+\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n+\tout = fdopen_lock_file(&lock, \"w\");\n+\tif (!out)\n+\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n+\n+\tin = fopen(alts, \"r\");\n+\tif (in) {\n+\t\tstruct strbuf line = STRBUF_INIT;\n+\n+\t\twhile (strbuf_getline(&line, in) != EOF) {\n+\t\t\tif (!strcmp(reference, line.buf)) {\n+\t\t\t\tfound = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n+\t\t}\n+\n+\t\tstrbuf_release(&line);\n+\t\tfclose(in);\n+\t}\n+\telse if (errno != ENOENT)\n+\t\tdie_errno(_(\"unable to read alternates file\"));\n+\n+\tif (found) {\n+\t\trollback_lock_file(&lock);\n+\t} else {\n+\t\tfprintf_or_die(out, \"%s\\n\", reference);\n+\t\tif (commit_lock_file(&lock))\n+\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n+\t\tif (the_repository->objects->loaded_alternates)\n+\t\t\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t\t\t     '\\n', NULL, 0);\n+\t}\n+\tfree(alts);\n+}\n+\n+void add_to_alternates_memory(const char *reference)\n+{\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t     '\\n', NULL, 0);\n+}\n+\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n+{\n+\tstruct object_directory *new_odb;\n+\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\t/*\n+\t * Make a new primary odb and link the old primary ODB in as an\n+\t * alternate\n+\t */\n+\tnew_odb = xcalloc(1, sizeof(*new_odb));\n+\tnew_odb->path = xstrdup(dir);\n+\n+\t/*\n+\t * Disable ref updates while a temporary odb is active, since\n+\t * the objects in the database may roll back.\n+\t */\n+\tnew_odb->disable_ref_updates = 1;\n+\tnew_odb->will_destroy = will_destroy;\n+\tnew_odb->next = the_repository->objects->odb;\n+\tthe_repository->objects->odb = new_odb;\n+\treturn new_odb->next;\n+}\n+\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n+{\n+\tstruct object_directory *cur_odb = the_repository->objects->odb;\n+\n+\tif (strcmp(old_path, cur_odb->path))\n+\t\tBUG(\"expected %s as primary object store; found %s\",\n+\t\t    old_path, cur_odb->path);\n+\n+\tif (cur_odb->next != restore_odb)\n+\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n+\n+\tthe_repository->objects->odb = restore_odb;\n+\tfree_object_directory(cur_odb);\n+}\n+\n+/*\n+ * Compute the exact path an alternate is at and returns it. In case of\n+ * error NULL is returned and the human readable error is added to `err`\n+ * `path` may be relative and should point to $GIT_DIR.\n+ * `err` must not be null.\n+ */\n+char *compute_alternate_path(const char *path, struct strbuf *err)\n+{\n+\tchar *ref_git = NULL;\n+\tconst char *repo;\n+\tint seen_error = 0;\n+\n+\tref_git = real_pathdup(path, 0);\n+\tif (!ref_git) {\n+\t\tseen_error = 1;\n+\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\trepo = read_gitfile(ref_git);\n+\tif (!repo)\n+\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n+\tif (repo) {\n+\t\tfree(ref_git);\n+\t\tref_git = xstrdup(repo);\n+\t}\n+\n+\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n+\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n+\t\tfree(ref_git);\n+\t\tref_git = ref_git_git;\n+\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tseen_error = 1;\n+\t\tif (get_common_dir(&sb, ref_git)) {\n+\t\t\tstrbuf_addf(err,\n+\t\t\t\t    _(\"reference repository '%s' as a linked \"\n+\t\t\t\t      \"checkout is not supported yet.\"),\n+\t\t\t\t    path);\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n+\t\t\t\t\t\"local repository.\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err,\n+\t\t\t    _(\"reference repository '%s' is grafted\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+out:\n+\tif (seen_error) {\n+\t\tFREE_AND_NULL(ref_git);\n+\t}\n+\n+\treturn ref_git;\n+}\n+\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n+{\n+\tstruct object_directory *odb;\n+\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n+\tstruct strbuf odb_path_real = STRBUF_INIT;\n+\n+\tprepare_alt_odb(r);\n+\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n+\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n+\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n+\t\t\tbreak;\n+\t}\n+\n+\tfree(obj_dir_real);\n+\tstrbuf_release(&odb_path_real);\n+\n+\tif (!odb)\n+\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n+\treturn odb;\n+}\n+\n+static void fill_alternate_refs_command(struct child_process *cmd,\n+\t\t\t\t\tconst char *repo_path)\n+{\n+\tconst char *value;\n+\n+\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n+\t\tcmd->use_shell = 1;\n+\n+\t\tstrvec_push(&cmd->args, value);\n+\t\tstrvec_push(&cmd->args, repo_path);\n+\t} else {\n+\t\tcmd->git_cmd = 1;\n+\n+\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n+\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n+\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n+\n+\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n+\t\t\tstrvec_push(&cmd->args, \"--\");\n+\t\t\tstrvec_split(&cmd->args, value);\n+\t\t}\n+\t}\n+\n+\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n+\tcmd->out = -1;\n+}\n+\n+static void read_alternate_refs(const char *path,\n+\t\t\t\talternate_ref_fn *cb,\n+\t\t\t\tvoid *data)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tstruct strbuf line = STRBUF_INIT;\n+\tFILE *fh;\n+\n+\tfill_alternate_refs_command(&cmd, path);\n+\n+\tif (start_command(&cmd))\n+\t\treturn;\n+\n+\tfh = xfdopen(cmd.out, \"r\");\n+\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n+\t\tstruct object_id oid;\n+\t\tconst char *p;\n+\n+\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n+\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n+\t\t\t\tline.buf);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcb(&oid, data);\n+\t}\n+\n+\tfclose(fh);\n+\tfinish_command(&cmd);\n+\tstrbuf_release(&line);\n+}\n+\n+struct alternate_refs_data {\n+\talternate_ref_fn *fn;\n+\tvoid *data;\n+};\n+\n+static int refs_from_alternate_cb(struct object_directory *e,\n+\t\t\t\t  void *data)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tsize_t base_len;\n+\tstruct alternate_refs_data *cb = data;\n+\n+\tif (!strbuf_realpath(&path, e->path, 0))\n+\t\tgoto out;\n+\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n+\t\tgoto out;\n+\tbase_len = path.len;\n+\n+\t/* Is this a git repository with refs? */\n+\tstrbuf_addstr(&path, \"/refs\");\n+\tif (!is_directory(path.buf))\n+\t\tgoto out;\n+\tstrbuf_setlen(&path, base_len);\n+\n+\tread_alternate_refs(path.buf, cb->fn, cb->data);\n+\n+out:\n+\tstrbuf_release(&path);\n+\treturn 0;\n+}\n+\n+void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n+{\n+\tstruct alternate_refs_data cb;\n+\tcb.fn = fn;\n+\tcb.data = data;\n+\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n+}\n+\n+int foreach_alt_odb(alt_odb_fn fn, void *cb)\n+{\n+\tstruct object_directory *ent;\n+\tint r = 0;\n+\n+\tprepare_alt_odb(the_repository);\n+\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n+\t\tr = fn(ent, cb);\n+\t\tif (r)\n+\t\t\tbreak;\n+\t}\n+\treturn r;\n+}\n+\n+void prepare_alt_odb(struct repository *r)\n+{\n+\tif (r->objects->loaded_alternates)\n+\t\treturn;\n+\n+\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n+\n+\tread_info_alternates(r, r->objects->odb->path, 0);\n+\tr->objects->loaded_alternates = 1;\n+}\n+\n+int has_alt_odb(struct repository *r)\n+{\n+\tprepare_alt_odb(r);\n+\treturn !!r->objects->odb->next;\n+}\n+\n+int obj_read_use_lock = 0;\n+pthread_mutex_t obj_read_mutex;\n+\n+void enable_obj_read_lock(void)\n+{\n+\tif (obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 1;\n+\tinit_recursive_mutex(&obj_read_mutex);\n+}\n+\n+void disable_obj_read_lock(void)\n+{\n+\tif (!obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 0;\n+\tpthread_mutex_destroy(&obj_read_mutex);\n+}\n+\n+int fetch_if_missing = 1;\n+\n+static int do_oid_object_info_extended(struct repository *r,\n+\t\t\t\t       const struct object_id *oid,\n+\t\t\t\t       struct object_info *oi, unsigned flags)\n+{\n+\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n+\tconst struct cached_object *co;\n+\tstruct pack_entry e;\n+\tint rtype;\n+\tconst struct object_id *real = oid;\n+\tint already_retried = 0;\n+\n+\n+\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n+\t\treal = lookup_replace_object(r, oid);\n+\n+\tif (is_null_oid(real))\n+\t\treturn -1;\n+\n+\tif (!oi)\n+\t\toi = &blank_oi;\n+\n+\tco = find_cached_object(real);\n+\tif (co) {\n+\t\tif (oi->typep)\n+\t\t\t*(oi->typep) = co->type;\n+\t\tif (oi->sizep)\n+\t\t\t*(oi->sizep) = co->size;\n+\t\tif (oi->disk_sizep)\n+\t\t\t*(oi->disk_sizep) = 0;\n+\t\tif (oi->delta_base_oid)\n+\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n+\t\tif (oi->type_name)\n+\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n+\t\tif (oi->contentp)\n+\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n+\t\toi->whence = OI_CACHED;\n+\t\treturn 0;\n+\t}\n+\n+\twhile (1) {\n+\t\tif (find_pack_entry(r, real, &e))\n+\t\t\tbreak;\n+\n+\t\t/* Most likely it's a loose object. */\n+\t\tif (!loose_object_info(r, real, oi, flags))\n+\t\t\treturn 0;\n+\n+\t\t/* Not a loose object; someone else may have just packed it. */\n+\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n+\t\t\treprepare_packed_git(r);\n+\t\t\tif (find_pack_entry(r, real, &e))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If r is the_repository, this might be an attempt at\n+\t\t * accessing a submodule object as if it were in the_repository\n+\t\t * (having called add_submodule_odb() on that submodule's ODB).\n+\t\t * If any such ODBs exist, register them and try again.\n+\t\t */\n+\t\tif (r == the_repository &&\n+\t\t    register_all_submodule_odb_as_alternates())\n+\t\t\t/* We added some alternates; retry */\n+\t\t\tcontinue;\n+\n+\t\t/* Check if it is a missing object */\n+\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n+\t\t    !already_retried &&\n+\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n+\t\t\tpromisor_remote_get_direct(r, real, 1);\n+\t\t\talready_retried = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n+\t\t\tconst struct packed_git *p;\n+\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n+\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n+\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n+\t\t\tif ((p = has_packed_and_bad(r, real)))\n+\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n+\t\t\t\t    oid_to_hex(real), p->pack_name);\n+\t\t}\n+\t\treturn -1;\n+\t}\n+\n+\tif (oi == &blank_oi)\n+\t\t/*\n+\t\t * We know that the caller doesn't actually need the\n+\t\t * information below, so return early.\n+\t\t */\n+\t\treturn 0;\n+\trtype = packed_object_info(r, e.p, e.offset, oi);\n+\tif (rtype < 0) {\n+\t\tmark_bad_packed_object(e.p, real);\n+\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n+\t} else if (oi->whence == OI_PACKED) {\n+\t\toi->u.packed.offset = e.offset;\n+\t\toi->u.packed.pack = e.p;\n+\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n+\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int oid_object_info_convert(struct repository *r,\n+\t\t\t\t   const struct object_id *input_oid,\n+\t\t\t\t   struct object_info *input_oi, unsigned flags)\n+{\n+\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n+\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n+\tstruct strbuf type_name = STRBUF_INIT;\n+\tstruct object_id oid, delta_base_oid;\n+\tstruct object_info new_oi, *oi;\n+\tunsigned long size;\n+\tvoid *content;\n+\tint ret;\n+\n+\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n+\t\tif (do_die)\n+\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Is new_oi needed? */\n+\toi = input_oi;\n+\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n+\t\t\t input_oi->contentp)) {\n+\t\tnew_oi = *input_oi;\n+\t\t/* Does delta_base_oid need to be converted? */\n+\t\tif (input_oi->delta_base_oid)\n+\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n+\t\t/* Will the attributes differ when converted? */\n+\t\tif (input_oi->sizep || input_oi->contentp) {\n+\t\t\tnew_oi.contentp = &content;\n+\t\t\tnew_oi.sizep = &size;\n+\t\t\tnew_oi.type_name = &type_name;\n+\t\t}\n+\t\toi = &new_oi;\n+\t}\n+\n+\tret = oid_object_info_extended(r, &oid, oi, flags);\n+\tif (ret)\n+\t\treturn -1;\n+\tif (oi == input_oi)\n+\t\treturn ret;\n+\n+\tif (new_oi.contentp) {\n+\t\tstruct strbuf outbuf = STRBUF_INIT;\n+\t\tenum object_type type;\n+\n+\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n+\t\t\t\t\t       !do_die);\n+\t\tif (type == -1)\n+\t\t\treturn -1;\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = convert_object_file(the_repository, &outbuf,\n+\t\t\t\t\t\t  the_hash_algo, input_algo,\n+\t\t\t\t\t\t  content, size, type, !do_die);\n+\t\t\tfree(content);\n+\t\t\tif (ret == -1)\n+\t\t\t\treturn -1;\n+\t\t\tsize = outbuf.len;\n+\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n+\t\t}\n+\t\tif (input_oi->sizep)\n+\t\t\t*input_oi->sizep = size;\n+\t\tif (input_oi->contentp)\n+\t\t\t*input_oi->contentp = content;\n+\t\telse\n+\t\t\tfree(content);\n+\t\tif (input_oi->type_name)\n+\t\t\t*input_oi->type_name = type_name;\n+\t\telse\n+\t\t\tstrbuf_release(&type_name);\n+\t}\n+\tif (new_oi.delta_base_oid == &delta_base_oid) {\n+\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n+\t\t\t\t input_oi->delta_base_oid)) {\n+\t\t\tif (do_die)\n+\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t\t    oid_to_hex(&delta_base_oid),\n+\t\t\t\t    input_algo->name);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\tinput_oi->whence = new_oi.whence;\n+\tinput_oi->u = new_oi.u;\n+\treturn ret;\n+}\n+\n+int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n+\t\t\t     struct object_info *oi, unsigned flags)\n+{\n+\tint ret;\n+\n+\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n+\t\treturn oid_object_info_convert(r, oid, oi, flags);\n+\n+\tobj_read_lock();\n+\tret = do_oid_object_info_extended(r, oid, oi, flags);\n+\tobj_read_unlock();\n+\treturn ret;\n+}\n+\n+\n+/* returns enum object_type or negative */\n+int oid_object_info(struct repository *r,\n+\t\t    const struct object_id *oid,\n+\t\t    unsigned long *sizep)\n+{\n+\tenum object_type type;\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\n+\toi.typep = &type;\n+\toi.sizep = sizep;\n+\tif (oid_object_info_extended(r, oid, &oi,\n+\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n+\t\treturn -1;\n+\treturn type;\n+}\n+\n+int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid)\n+{\n+\tstruct cached_object_entry *co;\n+\tchar *co_buf;\n+\n+\thash_object_file(the_hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(oid))\n+\t\treturn 0;\n+\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n+\tco = &cached_objects[cached_object_nr++];\n+\tco->value.size = len;\n+\tco->value.type = type;\n+\tco_buf = xmalloc(len);\n+\tmemcpy(co_buf, buf, len);\n+\tco->value.buf = co_buf;\n+\toidcpy(&co->oid, oid);\n+\treturn 0;\n+}\n+\n+/*\n+ * This function dies on corrupt objects; the callers who want to\n+ * deal with them should arrange to call oid_object_info_extended() and give\n+ * error messages themselves.\n+ */\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size)\n+{\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n+\tvoid *data;\n+\n+\toi.typep = type;\n+\toi.sizep = size;\n+\toi.contentp = &data;\n+\tif (oid_object_info_extended(r, oid, &oi, flags))\n+\t\treturn NULL;\n+\n+\treturn data;\n+}\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *actual_oid_return)\n+{\n+\tenum object_type type;\n+\tvoid *buffer;\n+\tunsigned long isize;\n+\tstruct object_id actual_oid;\n+\n+\toidcpy(&actual_oid, oid);\n+\twhile (1) {\n+\t\tint ref_length = -1;\n+\t\tconst char *ref_type = NULL;\n+\n+\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n+\t\tif (!buffer)\n+\t\t\treturn NULL;\n+\t\tif (type == required_type) {\n+\t\t\t*size = isize;\n+\t\t\tif (actual_oid_return)\n+\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n+\t\t\treturn buffer;\n+\t\t}\n+\t\t/* Handle references */\n+\t\telse if (type == OBJ_COMMIT)\n+\t\t\tref_type = \"tree \";\n+\t\telse if (type == OBJ_TAG)\n+\t\t\tref_type = \"object \";\n+\t\telse {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tref_length = strlen(ref_type);\n+\n+\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n+\t\t    memcmp(buffer, ref_type, ref_length) ||\n+\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tfree(buffer);\n+\t\t/* Now we have the ID of the referred-to object in\n+\t\t * actual_oid.  Check again. */\n+\t}\n+}\n+\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags)\n+{\n+\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n+\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n+\t\t(quick ? OBJECT_INFO_QUICK : 0);\n+\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n+}\n+\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags)\n+{\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n+}\n+\n+int repo_has_object_file(struct repository *r,\n+\t\t\t const struct object_id *oid)\n+{\n+\treturn repo_has_object_file_with_flags(r, oid, 0);\n+}\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect)\n+{\n+\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n+\tif (type < 0)\n+\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n+\tif (type != expect)\n+\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n+\t\t    type_name(expect));\n+}\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515853","messageId":"20250408-pks-split-object-file-v1-5-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 5/9] object-file: split up concerns of `HASH_*` flags","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:13Z","receivedAt":"2025-04-08T10:24:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The functions `hash_object_file()`, `write_object_file()` and\n`index_fd()` reuse the same set of flags to alter their behaviour. This\nnot only adds confusion, but given that every function only supports a\nsubset of the flags it becomes very hard to see which flags can be\npassed to what function. Last but not least, this entangles the\nimplementation of all three function families.\n\nSplit up concerns by creating separate flags for each of the function\nfamilies.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/hash-object.c  | 23 +++++++++++++++++------\n builtin/replace.c      |  2 +-\n builtin/update-index.c |  2 +-\n bulk-checkin.c         |  6 +++---\n cache-tree.c           |  2 +-\n notes-merge.c          |  2 +-\n object-file.c          | 18 +++++++++---------\n object-file.h          | 25 +++++++++++++++++++++----\n read-cache.c           |  4 ++--\n 9 files changed, 56 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex a25f0403f44..e7c0d6afdef 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -19,6 +19,11 @@\n #include \"strbuf.h\"\n #include \"write-or-die.h\"\n \n+enum {\n+\tHASH_OBJECT_CHECK = (1 << 0),\n+\tHASH_OBJECT_WRITE = (1 << 1),\n+};\n+\n /*\n  * This is to create corrupt objects for debugging and as such it\n  * needs to bypass the data conversion performed by, and the type\n@@ -33,7 +38,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n \t\tret = -1;\n \telse\n \t\tret = write_object_file_literally(buf.buf, buf.len, type, oid,\n-\t\t\t\t\t\t flags);\n+\t\t\t\t\t\t  (flags & HASH_OBJECT_WRITE) ? WRITE_OBJECT_FILE_PERSIST : 0);\n \tclose(fd);\n \tstrbuf_release(&buf);\n \treturn ret;\n@@ -42,15 +47,21 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n \t\t    int literally)\n {\n+\tunsigned int index_flags = 0;\n \tstruct stat st;\n \tstruct object_id oid;\n \n+\tif (flags & HASH_OBJECT_WRITE)\n+\t\tindex_flags |= INDEX_WRITE_OBJECT;\n+\tif (flags & HASH_OBJECT_CHECK)\n+\t\tindex_flags |= INDEX_FORMAT_CHECK;\n+\n \tif (fstat(fd, &st) < 0 ||\n \t    (literally\n \t     ? hash_literally(&oid, fd, type, flags)\n \t     : index_fd(the_repository->index, &oid, fd, &st,\n-\t\t\ttype_from_string(type), path, flags)))\n-\t\tdie((flags & HASH_WRITE_OBJECT)\n+\t\t\ttype_from_string(type), path, index_flags)))\n+\t\tdie((flags & HASH_OBJECT_WRITE)\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n \tprintf(\"%s\\n\", oid_to_hex(&oid));\n@@ -102,13 +113,13 @@ int cmd_hash_object(int argc,\n \tint no_filters = 0;\n \tint literally = 0;\n \tint nongit = 0;\n-\tunsigned flags = HASH_FORMAT_CHECK;\n+\tunsigned flags = HASH_OBJECT_CHECK;\n \tconst char *vpath = NULL;\n \tchar *vpath_free = NULL;\n \tconst struct option hash_object_options[] = {\n \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n-\t\t\tHASH_WRITE_OBJECT),\n+\t\t\tHASH_OBJECT_WRITE),\n \t\tOPT_COUNTUP( 0 , \"stdin\", &hashstdin, N_(\"read the object from stdin\")),\n \t\tOPT_BOOL( 0 , \"stdin-paths\", &stdin_paths, N_(\"read file names from stdin\")),\n \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n@@ -122,7 +133,7 @@ int cmd_hash_object(int argc,\n \targc = parse_options(argc, argv, prefix, hash_object_options,\n \t\t\t     hash_object_usage, 0);\n \n-\tif (flags & HASH_WRITE_OBJECT)\n+\tif (flags & HASH_OBJECT_WRITE)\n \t\tprefix = setup_git_directory();\n \telse\n \t\tprefix = setup_git_directory_gently(&nongit);\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 15ec0922ce1..2b4fc9a68b3 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -305,7 +305,7 @@ static int import_object(struct object_id *oid, enum object_type type,\n \t\tstrbuf_release(&result);\n \t} else {\n \t\tstruct stat st;\n-\t\tint flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;\n+\t\tint flags = INDEX_FORMAT_CHECK | INDEX_WRITE_OBJECT;\n \n \t\tif (fstat(fd, &st) < 0) {\n \t\t\terror_errno(_(\"unable to fstat %s\"), filename);\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex b2f6b1a3fbb..f0cf964294d 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -304,7 +304,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len\n \tce->ce_mode = ce_mode_from_stat(old, st->st_mode);\n \n \tif (index_path(the_repository->index, &ce->oid, path, st,\n-\t\t       info_only ? 0 : HASH_WRITE_OBJECT)) {\n+\t\t       info_only ? 0 : INDEX_WRITE_OBJECT)) {\n \t\tdiscard_cache_entry(ce);\n \t\treturn -1;\n \t}\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 23ac00ea0a6..309201a76a6 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -171,7 +171,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n \tunsigned char obuf[16384];\n \tunsigned hdrlen;\n \tint status = Z_OK;\n-\tint write_object = (flags & HASH_WRITE_OBJECT);\n+\tint write_object = (flags & INDEX_WRITE_OBJECT);\n \toff_t offset = 0;\n \n \tgit_deflate_init(&s, pack_compression_level);\n@@ -241,7 +241,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n static void prepare_to_stream(struct bulk_checkin_packfile *state,\n \t\t\t      unsigned flags)\n {\n-\tif (!(flags & HASH_WRITE_OBJECT) || state->f)\n+\tif (!(flags & INDEX_WRITE_OBJECT) || state->f)\n \t\treturn;\n \n \tstate->f = create_tmp_packfile(the_repository, &state->pack_tmp_name);\n@@ -275,7 +275,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \tgit_hash_update(&ctx, obuf, header_len);\n \n \t/* Note: idx is non-NULL when we are writing */\n-\tif ((flags & HASH_WRITE_OBJECT) != 0) {\n+\tif ((flags & INDEX_WRITE_OBJECT) != 0) {\n \t\tCALLOC_ARRAY(idx, 1);\n \n \t\tprepare_to_stream(state, flags);\ndiff --git a/cache-tree.c b/cache-tree.c\nindex bcbcad3d61a..4c8167ea927 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -452,7 +452,7 @@ static int update_one(struct cache_tree *it,\n \t\t\t\t OBJ_TREE, &it->oid);\n \t} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,\n \t\t\t\t\t   &it->oid, NULL, flags & WRITE_TREE_SILENT\n-\t\t\t\t\t   ? HASH_SILENT : 0)) {\n+\t\t\t\t\t   ? WRITE_OBJECT_FILE_SILENT : 0)) {\n \t\tstrbuf_release(&buffer);\n \t\treturn -1;\n \t}\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 5008faef450..d9f2914c819 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -729,7 +729,7 @@ int notes_merge_commit(struct notes_merge_options *o,\n \t\t/* write file as blob, and add to partial_tree */\n \t\tif (stat(path.buf, &st))\n \t\t\tdie_errno(\"Failed to stat '%s'\", path.buf);\n-\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))\n+\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, INDEX_WRITE_OBJECT))\n \t\t\tdie(\"Failed to write blob object from '%s'\", path.buf);\n \t\tif (add_note(partial_tree, &obj_oid, &blob_oid, NULL))\n \t\t\tdie(\"Failed to add resolved note '%s' to notes tree\",\ndiff --git a/object-file.c b/object-file.c\nindex 1c74aa239fb..dea5a70a331 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -33,9 +33,9 @@\n \n static int get_conv_flags(unsigned flags)\n {\n-\tif (flags & HASH_RENORMALIZE)\n+\tif (flags & INDEX_RENORMALIZE)\n \t\treturn CONV_EOL_RENORMALIZE;\n-\telse if (flags & HASH_WRITE_OBJECT)\n+\telse if (flags & INDEX_WRITE_OBJECT)\n \t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n \telse\n \t\treturn 0;\n@@ -835,7 +835,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n \n \tfd = create_tmpfile(tmp_file, filename);\n \tif (fd < 0) {\n-\t\tif (flags & HASH_SILENT)\n+\t\tif (flags & WRITE_OBJECT_FILE_SILENT)\n \t\t\treturn -1;\n \t\telse if (errno == EACCES)\n \t\t\treturn error(_(\"insufficient permission for adding \"\n@@ -967,7 +967,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,\n \t\tutb.actime = mtime;\n \t\tutb.modtime = mtime;\n \t\tif (utime(tmp_file.buf, &utb) < 0 &&\n-\t\t    !(flags & HASH_SILENT))\n+\t\t    !(flags & WRITE_OBJECT_FILE_SILENT))\n \t\t\twarning_errno(_(\"failed utime() on %s\"), tmp_file.buf);\n \t}\n \n@@ -1178,7 +1178,7 @@ int write_object_file_literally(const void *buf, unsigned long len,\n \twrite_object_file_prepare_literally(the_hash_algo, buf, len, type,\n \t\t\t\t\t    oid, header, &hdrlen);\n \n-\tif (!(flags & HASH_WRITE_OBJECT))\n+\tif (!(flags & WRITE_OBJECT_FILE_PERSIST))\n \t\tgoto cleanup;\n \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n \t\tgoto cleanup;\n@@ -1249,7 +1249,7 @@ static int index_mem(struct index_state *istate,\n {\n \tstruct strbuf nbuf = STRBUF_INIT;\n \tint ret = 0;\n-\tint write_object = flags & HASH_WRITE_OBJECT;\n+\tint write_object = flags & INDEX_WRITE_OBJECT;\n \n \tif (!type)\n \t\ttype = OBJ_BLOB;\n@@ -1264,7 +1264,7 @@ static int index_mem(struct index_state *istate,\n \t\t\tsize = nbuf.len;\n \t\t}\n \t}\n-\tif (flags & HASH_FORMAT_CHECK) {\n+\tif (flags & INDEX_FORMAT_CHECK) {\n \t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n \n \t\topts.strict = 1;\n@@ -1290,7 +1290,7 @@ static int index_stream_convert_blob(struct index_state *istate,\n \t\t\t\t     unsigned flags)\n {\n \tint ret = 0;\n-\tconst int write_object = flags & HASH_WRITE_OBJECT;\n+\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n \tstruct strbuf sbuf = STRBUF_INIT;\n \n \tassert(path);\n@@ -1422,7 +1422,7 @@ int index_path(struct index_state *istate, struct object_id *oid,\n \tcase S_IFLNK:\n \t\tif (strbuf_readlink(&sb, path, st->st_size))\n \t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n-\t\tif (!(flags & HASH_WRITE_OBJECT))\n+\t\tif (!(flags & INDEX_WRITE_OBJECT))\n \t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n \t\t\t\t\t OBJ_BLOB, oid);\n \t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\ndiff --git a/object-file.h b/object-file.h\nindex 78c84d970a9..c002fbe2345 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -14,10 +14,12 @@ struct index_state;\n  */\n extern int fetch_if_missing;\n \n-#define HASH_WRITE_OBJECT 1\n-#define HASH_FORMAT_CHECK 2\n-#define HASH_RENORMALIZE  4\n-#define HASH_SILENT 8\n+enum {\n+\tINDEX_WRITE_OBJECT = (1 << 0),\n+\tINDEX_FORMAT_CHECK = (1 << 1),\n+\tINDEX_RENORMALIZE  = (1 << 2),\n+};\n+\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n@@ -84,6 +86,21 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+enum {\n+\t/*\n+\t * By default, `write_object_file_literally()` does not actually write\n+\t * anything into the object store, but only computes the object ID.\n+\t * This flag changes that so that the object will be written as a loose\n+\t * object and persisted.\n+\t */\n+\tWRITE_OBJECT_FILE_PERSIST = (1 << 0),\n+\n+\t/*\n+\t * Do not print an error in case something gose wrong.\n+\t */\n+\tWRITE_OBJECT_FILE_SILENT = (1 << 1),\n+};\n+\n int write_object_file_flags(const void *buf, unsigned long len,\n \t\t\t    enum object_type type, struct object_id *oid,\n \t\t\t    struct object_id *comapt_oid_in, unsigned flags);\ndiff --git a/read-cache.c b/read-cache.c\nindex 2f9e21c897d..23028f43a11 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -706,11 +706,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \tint intent_only = flags & ADD_CACHE_INTENT;\n \tint add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|\n \t\t\t  (intent_only ? ADD_CACHE_NEW_ONLY : 0));\n-\tunsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;\n+\tunsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;\n \tstruct object_id oid;\n \n \tif (flags & ADD_CACHE_RENORMALIZE)\n-\t\thash_flags |= HASH_RENORMALIZE;\n+\t\thash_flags |= INDEX_RENORMALIZE;\n \n \tif (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))\n \t\treturn error(_(\"%s: can only add regular files, symbolic links or git-directories\"), path);\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515855","messageId":"20250408-pks-split-object-file-v1-6-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 6/9] object-file: split out functions relating to index subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:14Z","receivedAt":"2025-04-08T10:24:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Split out functions relating to the index subsystem from \"object-file.c\"\nto help us separate concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/difftool.c    |   2 +-\n builtin/hash-object.c |   1 +\n builtin/replace.c     |   1 +\n bulk-checkin.c        |   1 +\n diff.c                |   2 +-\n notes-merge.c         |   1 +\n object-file.c         | 225 --------------------------------------------------\n object-file.h         |  11 ---\n read-cache.c          | 224 +++++++++++++++++++++++++++++++++++++++++++++++++\n read-cache.h          |   9 ++\n 10 files changed, 239 insertions(+), 238 deletions(-)\n\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 41cd00066cc..45d6ea8a801 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -22,7 +22,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"parse-options.h\"\n-#include \"read-cache-ll.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"sparse-index.h\"\n #include \"strvec.h\"\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex e7c0d6afdef..7aa889b1b4a 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -15,6 +15,7 @@\n #include \"blob.h\"\n #include \"quote.h\"\n #include \"parse-options.h\"\n+#include \"read-cache.h\"\n #include \"setup.h\"\n #include \"strbuf.h\"\n #include \"write-or-die.h\"\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 2b4fc9a68b3..ce2948a9b2a 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -20,6 +20,7 @@\n #include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n+#include \"read-cache.h\"\n #include \"replace-object.h\"\n #include \"tag.h\"\n #include \"wildmatch.h\"\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 309201a76a6..0b9bad92868 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -11,6 +11,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"csum-file.h\"\n #include \"pack.h\"\ndiff --git a/diff.c b/diff.c\nindex 3bcf5028831..c361199ab1b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -42,7 +42,7 @@\n #include \"dir.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"read-cache-ll.h\"\n+#include \"read-cache.h\"\n #include \"setup.h\"\n #include \"strmap.h\"\n #include \"ws.h\"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex d9f2914c819..13750490eff 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -10,6 +10,7 @@\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\ndiff --git a/object-file.c b/object-file.c\nindex dea5a70a331..235d29858ff 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -12,9 +12,7 @@\n \n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n-#include \"convert.h\"\n #include \"environment.h\"\n-#include \"fsck.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"loose.h\"\n@@ -25,22 +23,11 @@\n #include \"pack.h\"\n #include \"packfile.h\"\n #include \"path.h\"\n-#include \"setup.h\"\n #include \"streaming.h\"\n \n /* The maximum size for an object header. */\n #define MAX_HEADER_LEN 32\n \n-static int get_conv_flags(unsigned flags)\n-{\n-\tif (flags & INDEX_RENORMALIZE)\n-\t\treturn CONV_EOL_RENORMALIZE;\n-\telse if (flags & INDEX_WRITE_OBJECT)\n-\t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n-\telse\n-\t\treturn 0;\n-}\n-\n static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n {\n \tint i;\n@@ -1225,218 +1212,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)\n \treturn ret;\n }\n \n-/*\n- * We can't use the normal fsck_error_function() for index_mem(),\n- * because we don't yet have a valid oid for it to report. Instead,\n- * report the minimal fsck error here, and rely on the caller to\n- * give more context.\n- */\n-static int hash_format_check_report(struct fsck_options *opts UNUSED,\n-\t\t\t\t    void *fsck_report UNUSED,\n-\t\t\t\t    enum fsck_msg_type msg_type UNUSED,\n-\t\t\t\t    enum fsck_msg_id msg_id UNUSED,\n-\t\t\t\t    const char *message)\n-{\n-\terror(_(\"object fails fsck: %s\"), message);\n-\treturn 1;\n-}\n-\n-static int index_mem(struct index_state *istate,\n-\t\t     struct object_id *oid,\n-\t\t     const void *buf, size_t size,\n-\t\t     enum object_type type,\n-\t\t     const char *path, unsigned flags)\n-{\n-\tstruct strbuf nbuf = STRBUF_INIT;\n-\tint ret = 0;\n-\tint write_object = flags & INDEX_WRITE_OBJECT;\n-\n-\tif (!type)\n-\t\ttype = OBJ_BLOB;\n-\n-\t/*\n-\t * Convert blobs to git internal format\n-\t */\n-\tif ((type == OBJ_BLOB) && path) {\n-\t\tif (convert_to_git(istate, path, buf, size, &nbuf,\n-\t\t\t\t   get_conv_flags(flags))) {\n-\t\t\tbuf = nbuf.buf;\n-\t\t\tsize = nbuf.len;\n-\t\t}\n-\t}\n-\tif (flags & INDEX_FORMAT_CHECK) {\n-\t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n-\n-\t\topts.strict = 1;\n-\t\topts.error_func = hash_format_check_report;\n-\t\tif (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts))\n-\t\t\tdie(_(\"refusing to create malformed object\"));\n-\t\tfsck_finish(&opts);\n-\t}\n-\n-\tif (write_object)\n-\t\tret = write_object_file(buf, size, type, oid);\n-\telse\n-\t\thash_object_file(the_hash_algo, buf, size, type, oid);\n-\n-\tstrbuf_release(&nbuf);\n-\treturn ret;\n-}\n-\n-static int index_stream_convert_blob(struct index_state *istate,\n-\t\t\t\t     struct object_id *oid,\n-\t\t\t\t     int fd,\n-\t\t\t\t     const char *path,\n-\t\t\t\t     unsigned flags)\n-{\n-\tint ret = 0;\n-\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n-\tstruct strbuf sbuf = STRBUF_INIT;\n-\n-\tassert(path);\n-\tassert(would_convert_to_git_filter_fd(istate, path));\n-\n-\tconvert_to_git_filter_fd(istate, path, fd, &sbuf,\n-\t\t\t\t get_conv_flags(flags));\n-\n-\tif (write_object)\n-\t\tret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,\n-\t\t\t\t\toid);\n-\telse\n-\t\thash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,\n-\t\t\t\t oid);\n-\tstrbuf_release(&sbuf);\n-\treturn ret;\n-}\n-\n-static int index_pipe(struct index_state *istate, struct object_id *oid,\n-\t\t      int fd, enum object_type type,\n-\t\t      const char *path, unsigned flags)\n-{\n-\tstruct strbuf sbuf = STRBUF_INIT;\n-\tint ret;\n-\n-\tif (strbuf_read(&sbuf, fd, 4096) >= 0)\n-\t\tret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);\n-\telse\n-\t\tret = -1;\n-\tstrbuf_release(&sbuf);\n-\treturn ret;\n-}\n-\n-#define SMALL_FILE_SIZE (32*1024)\n-\n-static int index_core(struct index_state *istate,\n-\t\t      struct object_id *oid, int fd, size_t size,\n-\t\t      enum object_type type, const char *path,\n-\t\t      unsigned flags)\n-{\n-\tint ret;\n-\n-\tif (!size) {\n-\t\tret = index_mem(istate, oid, \"\", size, type, path, flags);\n-\t} else if (size <= SMALL_FILE_SIZE) {\n-\t\tchar *buf = xmalloc(size);\n-\t\tssize_t read_result = read_in_full(fd, buf, size);\n-\t\tif (read_result < 0)\n-\t\t\tret = error_errno(_(\"read error while indexing %s\"),\n-\t\t\t\t\t  path ? path : \"<unknown>\");\n-\t\telse if (read_result != size)\n-\t\t\tret = error(_(\"short read while indexing %s\"),\n-\t\t\t\t    path ? path : \"<unknown>\");\n-\t\telse\n-\t\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n-\t\tfree(buf);\n-\t} else {\n-\t\tvoid *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n-\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n-\t\tmunmap(buf, size);\n-\t}\n-\treturn ret;\n-}\n-\n-/*\n- * This creates one packfile per large blob unless bulk-checkin\n- * machinery is \"plugged\".\n- *\n- * This also bypasses the usual \"convert-to-git\" dance, and that is on\n- * purpose. We could write a streaming version of the converting\n- * functions and insert that before feeding the data to fast-import\n- * (or equivalent in-core API described above). However, that is\n- * somewhat complicated, as we do not know the size of the filter\n- * result, which we need to know beforehand when writing a git object.\n- * Since the primary motivation for trying to stream from the working\n- * tree file and to avoid mmaping it in core is to deal with large\n- * binary blobs, they generally do not want to get any conversion, and\n- * callers should avoid this code path when filters are requested.\n- */\n-static int index_blob_stream(struct object_id *oid, int fd, size_t size,\n-\t\t\t     const char *path,\n-\t\t\t     unsigned flags)\n-{\n-\treturn index_blob_bulk_checkin(oid, fd, size, path, flags);\n-}\n-\n-int index_fd(struct index_state *istate, struct object_id *oid,\n-\t     int fd, struct stat *st,\n-\t     enum object_type type, const char *path, unsigned flags)\n-{\n-\tint ret;\n-\n-\t/*\n-\t * Call xsize_t() only when needed to avoid potentially unnecessary\n-\t * die() for large files.\n-\t */\n-\tif (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))\n-\t\tret = index_stream_convert_blob(istate, oid, fd, path, flags);\n-\telse if (!S_ISREG(st->st_mode))\n-\t\tret = index_pipe(istate, oid, fd, type, path, flags);\n-\telse if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) ||\n-\t\t type != OBJ_BLOB ||\n-\t\t (path && would_convert_to_git(istate, path)))\n-\t\tret = index_core(istate, oid, fd, xsize_t(st->st_size),\n-\t\t\t\t type, path, flags);\n-\telse\n-\t\tret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,\n-\t\t\t\t\tflags);\n-\tclose(fd);\n-\treturn ret;\n-}\n-\n-int index_path(struct index_state *istate, struct object_id *oid,\n-\t       const char *path, struct stat *st, unsigned flags)\n-{\n-\tint fd;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tint rc = 0;\n-\n-\tswitch (st->st_mode & S_IFMT) {\n-\tcase S_IFREG:\n-\t\tfd = open(path, O_RDONLY);\n-\t\tif (fd < 0)\n-\t\t\treturn error_errno(\"open(\\\"%s\\\")\", path);\n-\t\tif (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)\n-\t\t\treturn error(_(\"%s: failed to insert into database\"),\n-\t\t\t\t     path);\n-\t\tbreak;\n-\tcase S_IFLNK:\n-\t\tif (strbuf_readlink(&sb, path, st->st_size))\n-\t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n-\t\tif (!(flags & INDEX_WRITE_OBJECT))\n-\t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n-\t\t\t\t\t OBJ_BLOB, oid);\n-\t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\n-\t\t\trc = error(_(\"%s: failed to insert into database\"), path);\n-\t\tstrbuf_release(&sb);\n-\t\tbreak;\n-\tcase S_IFDIR:\n-\t\treturn repo_resolve_gitlink_ref(the_repository, path, \"HEAD\", oid);\n-\tdefault:\n-\t\treturn error(_(\"%s: unsupported file type\"), path);\n-\t}\n-\treturn rc;\n-}\n-\n int read_pack_header(int fd, struct pack_header *header)\n {\n \tif (read_in_full(fd, header, sizeof(*header)) != sizeof(*header))\ndiff --git a/object-file.h b/object-file.h\nindex c002fbe2345..7d48994f1b6 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -4,8 +4,6 @@\n #include \"git-zlib.h\"\n #include \"object.h\"\n \n-struct index_state;\n-\n /*\n  * Set this to 0 to prevent oid_object_info_extended() from fetching missing\n  * blobs. This has a difference only if extensions.partialClone is set.\n@@ -14,15 +12,6 @@ struct index_state;\n  */\n extern int fetch_if_missing;\n \n-enum {\n-\tINDEX_WRITE_OBJECT = (1 << 0),\n-\tINDEX_FORMAT_CHECK = (1 << 1),\n-\tINDEX_RENORMALIZE  = (1 << 2),\n-};\n-\n-int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n-int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n-\n struct object_directory;\n \n const char *odb_loose_path(struct object_directory *odb,\ndiff --git a/read-cache.c b/read-cache.c\nindex 23028f43a11..9909b56902f 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -10,9 +10,11 @@\n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n #include \"config.h\"\n+#include \"convert.h\"\n #include \"date.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n+#include \"fsck.h\"\n #include \"hex.h\"\n #include \"tempfile.h\"\n #include \"lockfile.h\"\n@@ -4007,3 +4009,225 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n+\n+static int get_conv_flags(unsigned flags)\n+{\n+\tif (flags & INDEX_RENORMALIZE)\n+\t\treturn CONV_EOL_RENORMALIZE;\n+\telse if (flags & INDEX_WRITE_OBJECT)\n+\t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n+\telse\n+\t\treturn 0;\n+}\n+\n+/*\n+ * We can't use the normal fsck_error_function() for index_mem(),\n+ * because we don't yet have a valid oid for it to report. Instead,\n+ * report the minimal fsck error here, and rely on the caller to\n+ * give more context.\n+ */\n+static int hash_format_check_report(struct fsck_options *opts UNUSED,\n+\t\t\t\t    void *fsck_report UNUSED,\n+\t\t\t\t    enum fsck_msg_type msg_type UNUSED,\n+\t\t\t\t    enum fsck_msg_id msg_id UNUSED,\n+\t\t\t\t    const char *message)\n+{\n+\terror(_(\"object fails fsck: %s\"), message);\n+\treturn 1;\n+}\n+\n+static int index_mem(struct index_state *istate,\n+\t\t     struct object_id *oid,\n+\t\t     const void *buf, size_t size,\n+\t\t     enum object_type type,\n+\t\t     const char *path, unsigned flags)\n+{\n+\tstruct strbuf nbuf = STRBUF_INIT;\n+\tint ret = 0;\n+\tint write_object = flags & INDEX_WRITE_OBJECT;\n+\n+\tif (!type)\n+\t\ttype = OBJ_BLOB;\n+\n+\t/*\n+\t * Convert blobs to git internal format\n+\t */\n+\tif ((type == OBJ_BLOB) && path) {\n+\t\tif (convert_to_git(istate, path, buf, size, &nbuf,\n+\t\t\t\t   get_conv_flags(flags))) {\n+\t\t\tbuf = nbuf.buf;\n+\t\t\tsize = nbuf.len;\n+\t\t}\n+\t}\n+\tif (flags & INDEX_FORMAT_CHECK) {\n+\t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n+\n+\t\topts.strict = 1;\n+\t\topts.error_func = hash_format_check_report;\n+\t\tif (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts))\n+\t\t\tdie(_(\"refusing to create malformed object\"));\n+\t\tfsck_finish(&opts);\n+\t}\n+\n+\tif (write_object)\n+\t\tret = write_object_file(buf, size, type, oid);\n+\telse\n+\t\thash_object_file(the_hash_algo, buf, size, type, oid);\n+\n+\tstrbuf_release(&nbuf);\n+\treturn ret;\n+}\n+\n+static int index_stream_convert_blob(struct index_state *istate,\n+\t\t\t\t     struct object_id *oid,\n+\t\t\t\t     int fd,\n+\t\t\t\t     const char *path,\n+\t\t\t\t     unsigned flags)\n+{\n+\tint ret = 0;\n+\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n+\tstruct strbuf sbuf = STRBUF_INIT;\n+\n+\tassert(path);\n+\tassert(would_convert_to_git_filter_fd(istate, path));\n+\n+\tconvert_to_git_filter_fd(istate, path, fd, &sbuf,\n+\t\t\t\t get_conv_flags(flags));\n+\n+\tif (write_object)\n+\t\tret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,\n+\t\t\t\t\toid);\n+\telse\n+\t\thash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,\n+\t\t\t\t oid);\n+\tstrbuf_release(&sbuf);\n+\treturn ret;\n+}\n+\n+static int index_pipe(struct index_state *istate, struct object_id *oid,\n+\t\t      int fd, enum object_type type,\n+\t\t      const char *path, unsigned flags)\n+{\n+\tstruct strbuf sbuf = STRBUF_INIT;\n+\tint ret;\n+\n+\tif (strbuf_read(&sbuf, fd, 4096) >= 0)\n+\t\tret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);\n+\telse\n+\t\tret = -1;\n+\tstrbuf_release(&sbuf);\n+\treturn ret;\n+}\n+\n+#define SMALL_FILE_SIZE (32*1024)\n+\n+static int index_core(struct index_state *istate,\n+\t\t      struct object_id *oid, int fd, size_t size,\n+\t\t      enum object_type type, const char *path,\n+\t\t      unsigned flags)\n+{\n+\tint ret;\n+\n+\tif (!size) {\n+\t\tret = index_mem(istate, oid, \"\", size, type, path, flags);\n+\t} else if (size <= SMALL_FILE_SIZE) {\n+\t\tchar *buf = xmalloc(size);\n+\t\tssize_t read_result = read_in_full(fd, buf, size);\n+\t\tif (read_result < 0)\n+\t\t\tret = error_errno(_(\"read error while indexing %s\"),\n+\t\t\t\t\t  path ? path : \"<unknown>\");\n+\t\telse if (read_result != size)\n+\t\t\tret = error(_(\"short read while indexing %s\"),\n+\t\t\t\t    path ? path : \"<unknown>\");\n+\t\telse\n+\t\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n+\t\tfree(buf);\n+\t} else {\n+\t\tvoid *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n+\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n+\t\tmunmap(buf, size);\n+\t}\n+\treturn ret;\n+}\n+\n+/*\n+ * This creates one packfile per large blob unless bulk-checkin\n+ * machinery is \"plugged\".\n+ *\n+ * This also bypasses the usual \"convert-to-git\" dance, and that is on\n+ * purpose. We could write a streaming version of the converting\n+ * functions and insert that before feeding the data to fast-import\n+ * (or equivalent in-core API described above). However, that is\n+ * somewhat complicated, as we do not know the size of the filter\n+ * result, which we need to know beforehand when writing a git object.\n+ * Since the primary motivation for trying to stream from the working\n+ * tree file and to avoid mmaping it in core is to deal with large\n+ * binary blobs, they generally do not want to get any conversion, and\n+ * callers should avoid this code path when filters are requested.\n+ */\n+static int index_blob_stream(struct object_id *oid, int fd, size_t size,\n+\t\t\t     const char *path,\n+\t\t\t     unsigned flags)\n+{\n+\treturn index_blob_bulk_checkin(oid, fd, size, path, flags);\n+}\n+\n+int index_fd(struct index_state *istate, struct object_id *oid,\n+\t     int fd, struct stat *st,\n+\t     enum object_type type, const char *path, unsigned flags)\n+{\n+\tint ret;\n+\n+\t/*\n+\t * Call xsize_t() only when needed to avoid potentially unnecessary\n+\t * die() for large files.\n+\t */\n+\tif (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))\n+\t\tret = index_stream_convert_blob(istate, oid, fd, path, flags);\n+\telse if (!S_ISREG(st->st_mode))\n+\t\tret = index_pipe(istate, oid, fd, type, path, flags);\n+\telse if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) ||\n+\t\t type != OBJ_BLOB ||\n+\t\t (path && would_convert_to_git(istate, path)))\n+\t\tret = index_core(istate, oid, fd, xsize_t(st->st_size),\n+\t\t\t\t type, path, flags);\n+\telse\n+\t\tret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,\n+\t\t\t\t\tflags);\n+\tclose(fd);\n+\treturn ret;\n+}\n+\n+int index_path(struct index_state *istate, struct object_id *oid,\n+\t       const char *path, struct stat *st, unsigned flags)\n+{\n+\tint fd;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint rc = 0;\n+\n+\tswitch (st->st_mode & S_IFMT) {\n+\tcase S_IFREG:\n+\t\tfd = open(path, O_RDONLY);\n+\t\tif (fd < 0)\n+\t\t\treturn error_errno(\"open(\\\"%s\\\")\", path);\n+\t\tif (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)\n+\t\t\treturn error(_(\"%s: failed to insert into database\"),\n+\t\t\t\t     path);\n+\t\tbreak;\n+\tcase S_IFLNK:\n+\t\tif (strbuf_readlink(&sb, path, st->st_size))\n+\t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n+\t\tif (!(flags & INDEX_WRITE_OBJECT))\n+\t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n+\t\t\t\t\t OBJ_BLOB, oid);\n+\t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\n+\t\t\trc = error(_(\"%s: failed to insert into database\"), path);\n+\t\tstrbuf_release(&sb);\n+\t\tbreak;\n+\tcase S_IFDIR:\n+\t\treturn repo_resolve_gitlink_ref(the_repository, path, \"HEAD\", oid);\n+\tdefault:\n+\t\treturn error(_(\"%s: unsupported file type\"), path);\n+\t}\n+\treturn rc;\n+}\ndiff --git a/read-cache.h b/read-cache.h\nindex 043da1f1aae..d655eb64bb7 100644\n--- a/read-cache.h\n+++ b/read-cache.h\n@@ -42,4 +42,13 @@ static inline int ce_path_match(struct index_state *istate,\n \t\t\t      S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));\n }\n \n+enum {\n+\tINDEX_WRITE_OBJECT = (1 << 0),\n+\tINDEX_FORMAT_CHECK = (1 << 1),\n+\tINDEX_RENORMALIZE  = (1 << 2),\n+};\n+\n+int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n+int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n+\n #endif /* READ_CACHE_H */\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515854","messageId":"20250408-pks-split-object-file-v1-7-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 7/9] object: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:15Z","receivedAt":"2025-04-08T10:24:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Split out functions relating to the object store subsystem from\n\"object.c\". This helps us to separate concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-store-ll.h |  3 ---\n object-store.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n object.c          | 67 -------------------------------------------------------\n 3 files changed, 66 insertions(+), 70 deletions(-)\n\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8ae80b8a5fa..8bb0f33f9a8 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -92,9 +92,6 @@ struct oidtree *odb_loose_cache(struct object_directory *odb,\n /* Empty the loose object cache for the specified object directory. */\n void odb_clear_loose_cache(struct object_directory *odb);\n \n-/* Clear and free the specified object directory */\n-void free_object_directory(struct object_directory *odb);\n-\n struct packed_git {\n \tstruct hashmap_entry packmap_ent;\n \tstruct packed_git *next;\ndiff --git a/object-store.c b/object-store.c\nindex e5f1f00cdde..ea2d86c429b 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -2,11 +2,13 @@\n \n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n+#include \"commit-graph.h\"\n #include \"config.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n+#include \"loose.h\"\n #include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n@@ -361,6 +363,14 @@ struct object_directory *set_temporary_primary_odb(const char *dir, int will_des\n \treturn new_odb->next;\n }\n \n+static void free_object_directory(struct object_directory *odb)\n+{\n+\tfree(odb->path);\n+\todb_clear_loose_cache(odb);\n+\tloose_object_map_clear(&odb->loose_map);\n+\tfree(odb);\n+}\n+\n void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n {\n \tstruct object_directory *cur_odb = the_repository->objects->odb;\n@@ -970,3 +980,59 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect)\n \t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n \t\t    type_name(expect));\n }\n+\n+struct raw_object_store *raw_object_store_new(void)\n+{\n+\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n+\n+\tmemset(o, 0, sizeof(*o));\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n+\tpthread_mutex_init(&o->replace_mutex, NULL);\n+\treturn o;\n+}\n+\n+static void free_object_directories(struct raw_object_store *o)\n+{\n+\twhile (o->odb) {\n+\t\tstruct object_directory *next;\n+\n+\t\tnext = o->odb->next;\n+\t\tfree_object_directory(o->odb);\n+\t\to->odb = next;\n+\t}\n+\tkh_destroy_odb_path_map(o->odb_by_path);\n+\to->odb_by_path = NULL;\n+}\n+\n+void raw_object_store_clear(struct raw_object_store *o)\n+{\n+\tFREE_AND_NULL(o->alternate_db);\n+\n+\toidmap_free(o->replace_map, 1);\n+\tFREE_AND_NULL(o->replace_map);\n+\tpthread_mutex_destroy(&o->replace_mutex);\n+\n+\tfree_commit_graph(o->commit_graph);\n+\to->commit_graph = NULL;\n+\to->commit_graph_attempted = 0;\n+\n+\tfree_object_directories(o);\n+\to->odb_tail = NULL;\n+\to->loaded_alternates = 0;\n+\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\tclose_object_store(o);\n+\n+\t/*\n+\t * `close_object_store()` only closes the packfiles, but doesn't free\n+\t * them. We thus have to do this manually.\n+\t */\n+\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n+\t\tnext = p->next;\n+\t\tfree(p);\n+\t}\n+\to->packed_git = NULL;\n+\n+\thashmap_clear(&o->pack_map);\n+}\ndiff --git a/object.c b/object.c\nindex 154525a4972..ccda798b75f 100644\n--- a/object.c\n+++ b/object.c\n@@ -6,16 +6,13 @@\n #include \"object.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store.h\"\n #include \"blob.h\"\n #include \"statinfo.h\"\n #include \"tree.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"alloc.h\"\n-#include \"packfile.h\"\n #include \"commit-graph.h\"\n-#include \"loose.h\"\n \n unsigned int get_max_object_index(const struct repository *repo)\n {\n@@ -567,70 +564,6 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo)\n \treturn o;\n }\n \n-struct raw_object_store *raw_object_store_new(void)\n-{\n-\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n-\n-\tmemset(o, 0, sizeof(*o));\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n-\tpthread_mutex_init(&o->replace_mutex, NULL);\n-\treturn o;\n-}\n-\n-void free_object_directory(struct object_directory *odb)\n-{\n-\tfree(odb->path);\n-\todb_clear_loose_cache(odb);\n-\tloose_object_map_clear(&odb->loose_map);\n-\tfree(odb);\n-}\n-\n-static void free_object_directories(struct raw_object_store *o)\n-{\n-\twhile (o->odb) {\n-\t\tstruct object_directory *next;\n-\n-\t\tnext = o->odb->next;\n-\t\tfree_object_directory(o->odb);\n-\t\to->odb = next;\n-\t}\n-\tkh_destroy_odb_path_map(o->odb_by_path);\n-\to->odb_by_path = NULL;\n-}\n-\n-void raw_object_store_clear(struct raw_object_store *o)\n-{\n-\tFREE_AND_NULL(o->alternate_db);\n-\n-\toidmap_free(o->replace_map, 1);\n-\tFREE_AND_NULL(o->replace_map);\n-\tpthread_mutex_destroy(&o->replace_mutex);\n-\n-\tfree_commit_graph(o->commit_graph);\n-\to->commit_graph = NULL;\n-\to->commit_graph_attempted = 0;\n-\n-\tfree_object_directories(o);\n-\to->odb_tail = NULL;\n-\to->loaded_alternates = 0;\n-\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\tclose_object_store(o);\n-\n-\t/*\n-\t * `close_object_store()` only closes the packfiles, but doesn't free\n-\t * them. We thus have to do this manually.\n-\t */\n-\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n-\t\tnext = p->next;\n-\t\tfree(p);\n-\t}\n-\to->packed_git = NULL;\n-\n-\thashmap_clear(&o->pack_map);\n-}\n-\n void parsed_object_pool_reset_commit_grafts(struct parsed_object_pool *o)\n {\n \tfor (int i = 0; i < o->grafts_nr; i++) {\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515857","messageId":"20250408-pks-split-object-file-v1-8-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 8/9] object-store: remove global array of cached objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:16Z","receivedAt":"2025-04-08T10:24:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Cached objects are virtual objects that can be set up without writing\nanything into the object store directly. This mechanism for example\nallows us to create fake commits in git-blame(1).\n\nThe cached objects are stored in a global variable. Refactor the code so\nthat we instead store the array as part of the raw object store. This is\nanother step into the direction of libifying our object database.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n blame.c           |  2 +-\n object-store-ll.h | 14 +++++++++++++-\n object-store.c    | 39 +++++++++++++++++++++++----------------\n 3 files changed, 37 insertions(+), 18 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 703dab43e78..b7c5bd692e6 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -277,7 +277,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,\n \tconvert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);\n \torigin->file.ptr = buf.buf;\n \torigin->file.size = buf.len;\n-\tpretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n+\tpretend_object_file(the_repository, buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n \n \t/*\n \t * Read the current index, replace the path entry with\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8bb0f33f9a8..bb5e8798a1b 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -151,6 +151,8 @@ static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n }\n \n+struct cached_object_entry;\n+\n struct raw_object_store {\n \t/*\n \t * Set of all object directories; the main directory is first (and\n@@ -203,6 +205,15 @@ struct raw_object_store {\n \t\tunsigned flags;\n \t} kept_pack_cache;\n \n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n \t/*\n \t * A map of packfiles to packed_git structs for tracking which\n \t * packs have been loaded already.\n@@ -272,7 +283,8 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n  * object in persistent storage before writing any other new objects\n  * that reference it.\n  */\n-int pretend_object_file(void *, unsigned long, enum object_type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid);\n \n struct object_info {\ndiff --git a/object-store.c b/object-store.c\nindex ea2d86c429b..17fa06a86fa 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -30,31 +30,31 @@\n  * to write them into the object store (e.g. a browse-only\n  * application).\n  */\n-static struct cached_object_entry {\n+struct cached_object_entry {\n \tstruct object_id oid;\n \tstruct cached_object {\n \t\tenum object_type type;\n \t\tconst void *buf;\n \t\tunsigned long size;\n \t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n+};\n \n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n+static const struct cached_object *find_cached_object(struct raw_object_store *object_store,\n+\t\t\t\t\t\t      const struct object_id *oid)\n {\n \tstatic const struct cached_object empty_tree = {\n \t\t.type = OBJ_TREE,\n \t\t.buf = \"\",\n \t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n+\tconst struct cached_object_entry *co = object_store->cached_objects;\n \n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\tfor (size_t i = 0; i < object_store->cached_object_nr; i++, co++)\n \t\tif (oideq(&co->oid, oid))\n \t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n+\n+\tif (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))\n \t\treturn &empty_tree;\n+\n \treturn NULL;\n }\n \n@@ -650,7 +650,7 @@ static int do_oid_object_info_extended(struct repository *r,\n \tif (!oi)\n \t\toi = &blank_oi;\n \n-\tco = find_cached_object(real);\n+\tco = find_cached_object(r->objects, real);\n \tif (co) {\n \t\tif (oi->typep)\n \t\t\t*(oi->typep) = co->type;\n@@ -853,18 +853,21 @@ int oid_object_info(struct repository *r,\n \treturn type;\n }\n \n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid)\n {\n \tstruct cached_object_entry *co;\n \tchar *co_buf;\n \n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n+\thash_object_file(repo->hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(repo, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(repo->objects, oid))\n \t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n+\n+\tALLOC_GROW(repo->objects->cached_objects,\n+\t\t   repo->objects->cached_object_nr + 1, repo->objects->cached_object_alloc);\n+\tco = &repo->objects->cached_objects[repo->objects->cached_object_nr++];\n \tco->value.size = len;\n \tco->value.type = type;\n \tco_buf = xmalloc(len);\n@@ -1021,6 +1024,10 @@ void raw_object_store_clear(struct raw_object_store *o)\n \to->odb_tail = NULL;\n \to->loaded_alternates = 0;\n \n+\tfor (size_t i = 0; i < o->cached_object_nr; i++)\n+\t\tfree((char *) o->cached_objects[i].value.buf);\n+\tFREE_AND_NULL(o->cached_objects);\n+\n \tINIT_LIST_HEAD(&o->packed_git_mru);\n \tclose_object_store(o);\n \n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515858","messageId":"20250408-pks-split-object-file-v1-9-f1fd50191143@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH 9/9] object-store: merge \"object-store-ll.h\" and \"object-store.h\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-08T10:24:17Z","receivedAt":"2025-04-08T10:24:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"object-store-ll.h\" header has been introduced to keep transitive\nheader dependendcies and compile times at bay. Now that we have created\na new \"object-store.c\" file though we can easily move the last remaining\nadditional bit of \"object-store.h\", the `odb_path_map`, out of the\nheader.\n\nDo so. As the \"object-store.h\" header is now equivalent to its low-level\nalternative we drop the latter and inline it into the former.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n apply.c                       |   2 +-\n archive-tar.c                 |   2 +-\n archive-zip.c                 |   2 +-\n archive.c                     |   2 +-\n attr.c                        |   2 +-\n bisect.c                      |   2 +-\n blame.c                       |   2 +-\n builtin/backfill.c            |   2 +-\n builtin/blame.c               |   2 +-\n builtin/cat-file.c            |   2 +-\n builtin/checkout.c            |   2 +-\n builtin/clone.c               |   2 +-\n builtin/commit-graph.c        |   2 +-\n builtin/commit-tree.c         |   2 +-\n builtin/count-objects.c       |   2 +-\n builtin/describe.c            |   2 +-\n builtin/difftool.c            |   2 +-\n builtin/fast-export.c         |   2 +-\n builtin/fast-import.c         |   2 +-\n builtin/fetch.c               |   2 +-\n builtin/fsck.c                |   2 +-\n builtin/gc.c                  |   2 +-\n builtin/grep.c                |   2 +-\n builtin/hash-object.c         |   2 +-\n builtin/index-pack.c          |   2 +-\n builtin/log.c                 |   2 +-\n builtin/ls-tree.c             |   2 +-\n builtin/merge-tree.c          |   2 +-\n builtin/mktag.c               |   2 +-\n builtin/mktree.c              |   2 +-\n builtin/multi-pack-index.c    |   2 +-\n builtin/notes.c               |   2 +-\n builtin/pack-objects.c        |   2 +-\n builtin/pack-redundant.c      |   2 +-\n builtin/prune.c               |   2 +-\n builtin/receive-pack.c        |   2 +-\n builtin/remote.c              |   2 +-\n builtin/repack.c              |   2 +-\n builtin/replace.c             |   2 +-\n builtin/rev-list.c            |   2 +-\n builtin/show-ref.c            |   2 +-\n builtin/submodule--helper.c   |   2 +-\n builtin/tag.c                 |   2 +-\n builtin/unpack-file.c         |   2 +-\n builtin/unpack-objects.c      |   2 +-\n bulk-checkin.c                |   2 +-\n bundle-uri.c                  |   2 +-\n bundle.c                      |   2 +-\n cache-tree.c                  |   2 +-\n combine-diff.c                |   2 +-\n commit-graph.c                |   2 +-\n commit-graph.h                |   2 +-\n commit.c                      |   2 +-\n config.c                      |   2 +-\n connected.c                   |   2 +-\n convert.c                     |   2 +-\n diagnose.c                    |   2 +-\n diff.c                        |   2 +-\n diffcore-rename.c             |   2 +-\n dir.c                         |   2 +-\n entry.c                       |   2 +-\n fetch-pack.c                  |   2 +-\n fmt-merge-msg.c               |   2 +-\n fsck.c                        |   2 +-\n grep.c                        |   2 +-\n http-backend.c                |   2 +-\n http-push.c                   |   2 +-\n http-walker.c                 |   2 +-\n http.c                        |   2 +-\n list-objects-filter.c         |   2 +-\n list-objects.c                |   2 +-\n log-tree.c                    |   2 +-\n mailmap.c                     |   2 +-\n merge-blobs.c                 |   2 +-\n merge-recursive.c             |   2 +-\n notes-merge.c                 |   2 +-\n object-file.c                 |   1 +\n object-name.c                 |   2 +-\n object-store-ll.h             | 517 ------------------------------------------\n object-store.c                |   5 +\n object-store.h                | 516 ++++++++++++++++++++++++++++++++++++++++-\n oss-fuzz/fuzz-pack-idx.c      |   2 +-\n pack-bitmap-write.c           |   2 +-\n pack-bitmap.c                 |   2 +-\n pack-check.c                  |   2 +-\n pack-mtimes.c                 |   2 +-\n pack-objects.h                |   2 +-\n pack-revindex.c               |   2 +-\n packfile.c                    |   2 +-\n path.c                        |   2 +-\n promisor-remote.c             |   2 +-\n protocol-caps.c               |   2 +-\n prune-packed.c                |   2 +-\n reachable.c                   |   2 +-\n read-cache.c                  |   2 +-\n ref-filter.c                  |   2 +-\n reflog.c                      |   2 +-\n refs.c                        |   2 +-\n remote.c                      |   2 +-\n replace-object.c              |   2 +-\n replace-object.h              |   2 +-\n repository.c                  |   2 +-\n rerere.c                      |   2 +-\n revision.c                    |   2 +-\n send-pack.c                   |   2 +-\n sequencer.c                   |   2 +-\n server-info.c                 |   2 +-\n shallow.c                     |   2 +-\n streaming.c                   |   2 +-\n submodule-config.c            |   2 +-\n submodule.c                   |   2 +-\n t/helper/test-pack-mtimes.c   |   2 +-\n t/helper/test-partial-clone.c |   2 +-\n t/helper/test-read-graph.c    |   2 +-\n t/helper/test-read-midx.c     |   2 +-\n t/helper/test-ref-store.c     |   2 +-\n tag.c                         |   2 +-\n tmp-objdir.c                  |   2 +-\n tree-walk.c                   |   2 +-\n tree.c                        |   2 +-\n unpack-trees.c                |   2 +-\n upload-pack.c                 |   2 +-\n walker.c                      |   2 +-\n xdiff-interface.c             |   2 +-\n 124 files changed, 637 insertions(+), 642 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex f274a379487..2b6f4d0af87 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -14,7 +14,7 @@\n #include \"abspath.h\"\n #include \"base85.h\"\n #include \"config.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"delta.h\"\n #include \"diff.h\"\n #include \"dir.h\"\ndiff --git a/archive-tar.c b/archive-tar.c\nindex 0edf13fba75..282b48196f9 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -11,7 +11,7 @@\n #include \"hex.h\"\n #include \"tar.h\"\n #include \"archive.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"streaming.h\"\n #include \"run-command.h\"\ndiff --git a/archive-zip.c b/archive-zip.c\nindex 9f32730181b..405da6f3d83 100644\n--- a/archive-zip.c\n+++ b/archive-zip.c\n@@ -12,7 +12,7 @@\n #include \"hex.h\"\n #include \"streaming.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"userdiff.h\"\n #include \"write-or-die.h\"\ndiff --git a/archive.c b/archive.c\nindex c95e3981524..014c312178c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -14,7 +14,7 @@\n #include \"pretty.h\"\n #include \"setup.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\ndiff --git a/attr.c b/attr.c\nindex 0bd2750528f..86b6109fc4e 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -22,7 +22,7 @@\n #include \"read-cache-ll.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/bisect.c b/bisect.c\nindex 269a98bf978..a327468c75b 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -20,7 +20,7 @@\n #include \"commit-slab.h\"\n #include \"commit-reach.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"dir.h\"\n \ndiff --git a/blame.c b/blame.c\nindex b7c5bd692e6..57daa45e899 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"cache-tree.h\"\n #include \"mergesort.h\"\n #include \"commit.h\"\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex 33e1ea2f84f..aaa104bc91d 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -13,7 +13,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"promisor-remote.h\"\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c470654c7ec..4e156bfd19d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -28,7 +28,7 @@\n #include \"line-log.h\"\n #include \"progress.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"blame.h\"\n #include \"refs.h\"\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex b13561cf73b..c870fde260a 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -22,7 +22,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"promisor-remote.h\"\n #include \"mailmap.h\"\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3e68623838a..8136962e2b3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -20,7 +20,7 @@\n #include \"merge-recursive.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"preload-index.h\"\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 2993acb630e..d0423a2198b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -25,7 +25,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\ndiff --git a/builtin/commit-graph.c b/builtin/commit-graph.c\nindex 8ca75262c59..be06d0a811b 100644\n--- a/builtin/commit-graph.c\n+++ b/builtin/commit-graph.c\n@@ -6,7 +6,7 @@\n #include \"hex.h\"\n #include \"parse-options.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"progress.h\"\n #include \"replace-object.h\"\n #include \"strbuf.h\"\ndiff --git a/builtin/commit-tree.c b/builtin/commit-tree.c\nindex 38457600a4e..6f9975e7a88 100644\n--- a/builtin/commit-tree.c\n+++ b/builtin/commit-tree.c\n@@ -9,7 +9,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n #include \"commit.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 1e89148ed74..0bb5360b2f2 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -12,7 +12,7 @@\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static unsigned long garbage;\n static off_t size_garbage;\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 23df333fd04..0f87fbceef3 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -19,7 +19,7 @@\n #include \"setup.h\"\n #include \"strvec.h\"\n #include \"run-command.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects.h\"\n #include \"commit-slab.h\"\n #include \"wildmatch.h\"\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 45d6ea8a801..e6e45436531 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -29,7 +29,7 @@\n #include \"strbuf.h\"\n #include \"lockfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"entry.h\"\n #include \"setup.h\"\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 170126d41ac..afacd228b5d 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 63880b595cc..0357211ea6c 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -24,7 +24,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"mem-pool.h\"\n #include \"commit-reach.h\"\n #include \"khash.h\"\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 02af5054690..b52a32a5e0d 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"oid-array.h\"\n #include \"commit.h\"\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 32d40d8f9fc..47055ebaf9a 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -18,7 +18,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex b069629676c..def922e7145 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -29,7 +29,7 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\n #include \"path.h\"\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 283d64cab80..bcfbe5be5ba 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -26,7 +26,7 @@\n #include \"submodule-config.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pager.h\"\n #include \"path.h\"\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 7aa889b1b4a..e4eddd63bf4 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -11,7 +11,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"blob.h\"\n #include \"quote.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex de127c0ff13..60a8ee05dbc 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -21,7 +21,7 @@\n #include \"packfile.h\"\n #include \"pack-revindex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"path.h\"\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 06ffaa93e86..72ac45e9433 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -16,7 +16,7 @@\n #include \"hex.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"color.h\"\n #include \"commit.h\"\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8542b5d53e4..8aafc30ca48 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -10,7 +10,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"path.h\"\n #include \"quote.h\"\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 3ec7127b3a6..4aafa73c615 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -10,7 +10,7 @@\n #include \"commit-reach.h\"\n #include \"merge-ort.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 6e188dce50a..7ac11c46d53 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -6,7 +6,7 @@\n #include \"strbuf.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"fsck.h\"\n #include \"config.h\"\n \ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 0644f951161..7ffe6eefd8a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -12,7 +12,7 @@\n #include \"tree.h\"\n #include \"parse-options.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct treeent {\n \tunsigned mode;\ndiff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\nindex 2a938466f53..d98410ca6c6 100644\n--- a/builtin/multi-pack-index.c\n+++ b/builtin/multi-pack-index.c\n@@ -7,7 +7,7 @@\n #include \"midx.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 0dbc233752d..a3f433ca4c0 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -16,7 +16,7 @@\n #include \"notes.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n \n #include \"pretty.h\"\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 163aab547fe..488c80f2cf3 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -32,7 +32,7 @@\n #include \"list.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"dir.h\"\n #include \"midx.h\"\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex 3febe732f8e..5d1fc781761 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -13,7 +13,7 @@\n #include \"hex.h\"\n \n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n \n #define BLKSIZE 512\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex 8f52da8bd66..e930caa0c0a 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -17,7 +17,7 @@\n #include \"replace-object.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"shallow.h\"\n \n static const char * const prune_usage[] = {\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex ee51bd76f60..be314879e82 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -33,7 +33,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"protocol.h\"\n #include \"commit-reach.h\"\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 1b7aad88380..59481b3a82c 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"rebase.h\"\n #include \"refs.h\"\n #include \"refspec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strvec.h\"\n #include \"commit-reach.h\"\n #include \"progress.h\"\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f3330ade7b8..1fd2874324a 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -17,7 +17,7 @@\n #include \"midx.h\"\n #include \"packfile.h\"\n #include \"prune-packed.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"shallow.h\"\n #include \"pack.h\"\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex ce2948a9b2a..4204ca3605b 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache.h\"\n #include \"replace-object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex bb26bee0d45..0170d79b62f 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -14,7 +14,7 @@\n #include \"object.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"log-tree.h\"\n #include \"graph.h\"\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 285cd3e4338..f81209f23c3 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"refs/refs-internal.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 570226ea166..a8d1200b7cb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -28,7 +28,7 @@\n #include \"diff.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"advice.h\"\n #include \"branch.h\"\n #include \"list-objects-filter-options.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 536a01ff3ae..e6b372cebf5 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -19,7 +19,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"tag.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex b19e5cabd03..e33acfc4ee4 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static char *create_temp_file(struct object_id *oid)\n {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 4078eab9252..661be789f13 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -9,7 +9,7 @@\n #include \"git-zlib.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"delta.h\"\n #include \"pack.h\"\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 0b9bad92868..b38937d739b 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -19,7 +19,7 @@\n #include \"tmp-objdir.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int odb_transaction_nesting;\n \ndiff --git a/bundle-uri.c b/bundle-uri.c\nindex 744257c49c1..96d2ba726d9 100644\n--- a/bundle-uri.c\n+++ b/bundle-uri.c\n@@ -14,7 +14,7 @@\n #include \"fetch-pack.h\"\n #include \"remote.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct {\n \tenum bundle_list_heuristic heuristic;\ndiff --git a/bundle.c b/bundle.c\nindex d7ad6908433..d661c4ec214 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -7,7 +7,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 4c8167ea927..c0e1e9ee1d4 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -10,7 +10,7 @@\n #include \"cache-tree.h\"\n #include \"bulk-checkin.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 553bf59fed6..dfae9f7995d 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -2,7 +2,7 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"convert.h\"\n #include \"diff.h\"\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 8060c358b84..a36c2f1e303 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"alloc.h\"\ndiff --git a/commit-graph.h b/commit-graph.h\nindex 67819401954..13f662827d4 100644\n--- a/commit-graph.h\n+++ b/commit-graph.h\n@@ -1,7 +1,7 @@\n #ifndef COMMIT_GRAPH_H\n #define COMMIT_GRAPH_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n \n #define GIT_TEST_COMMIT_GRAPH \"GIT_TEST_COMMIT_GRAPH\"\ndiff --git a/commit.c b/commit.c\nindex fbf4f8e87fd..00842678bd2 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -9,7 +9,7 @@\n #include \"hex.h\"\n #include \"repository.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/config.c b/config.c\nindex accb47e2d18..b18b5617fcd 100644\n--- a/config.c\n+++ b/config.c\n@@ -31,7 +31,7 @@\n #include \"hashmap.h\"\n #include \"string-list.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"path.h\"\n #include \"utf8.h\"\ndiff --git a/connected.c b/connected.c\nindex 3099da84f33..4415388beba 100644\n--- a/connected.c\n+++ b/connected.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n #include \"connected.h\"\ndiff --git a/convert.c b/convert.c\nindex 9cc0ca20ca0..8783e17941f 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -8,7 +8,7 @@\n #include \"copy.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"attr.h\"\n #include \"run-command.h\"\n #include \"quote.h\"\ndiff --git a/diagnose.c b/diagnose.c\nindex bd485effea2..b1be74be983 100644\n--- a/diagnose.c\n+++ b/diagnose.c\n@@ -7,7 +7,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"strvec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"parse-options.h\"\n #include \"repository.h\"\ndiff --git a/diff.c b/diff.c\nindex c361199ab1b..9f2042fd1a5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -23,7 +23,7 @@\n #include \"color.h\"\n #include \"run-command.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"userdiff.h\"\n #include \"submodule.h\"\n #include \"hashmap.h\"\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 5002e896aad..787a2cef5f4 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -8,7 +8,7 @@\n #include \"git-compat-util.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"hashmap.h\"\n #include \"mem-pool.h\"\n #include \"oid-array.h\"\ndiff --git a/dir.c b/dir.c\nindex 16ae3b5169d..1f8b6e2aa67 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/entry.c b/entry.c\nindex 81b321e53d1..f36ec5ad242 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -1,7 +1,7 @@\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 1ed5e11dd56..210dc30d50f 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -24,7 +24,7 @@\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"connected.h\"\n #include \"fetch-negotiator.h\"\ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex 5b63c3b088a..501b5acdd44 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -6,7 +6,7 @@\n #include \"environment.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"diff.h\"\n #include \"diff-merges.h\"\n #include \"hex.h\"\ndiff --git a/fsck.c b/fsck.c\nindex 9fc4c25ffd5..8dc8472ceb3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"dir.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"object.h\"\ndiff --git a/grep.c b/grep.c\nindex 9284b5741f5..f8d535182c3 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"grep.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n #include \"xdiff-interface.h\"\ndiff --git a/http-backend.c b/http-backend.c\nindex 50b2858fad6..0c575aa88aa 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -18,7 +18,7 @@\n #include \"url.h\"\n #include \"strvec.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"protocol.h\"\n #include \"date.h\"\n #include \"write-or-die.h\"\ndiff --git a/http-push.c b/http-push.c\nindex 806eb67cf1b..32e37565f4e 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -20,7 +20,7 @@\n #include \"url.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n \n #ifdef EXPAT_NEEDS_XMLPARSE_H\ndiff --git a/http-walker.c b/http-walker.c\nindex 7918ddc0968..882cae19c24 100644\n--- a/http-walker.c\n+++ b/http-walker.c\n@@ -9,7 +9,7 @@\n #include \"list.h\"\n #include \"transport.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct alt_base {\n \tchar *base;\ndiff --git a/http.c b/http.c\nindex 0c9a872809f..bc18ff83c4b 100644\n--- a/http.c\n+++ b/http.c\n@@ -19,7 +19,7 @@\n #include \"packfile.h\"\n #include \"string-list.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tempfile.h\"\n \n static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);\ndiff --git a/list-objects-filter.c b/list-objects-filter.c\nindex dc598a081bb..7765761b3c6 100644\n--- a/list-objects-filter.c\n+++ b/list-objects-filter.c\n@@ -12,7 +12,7 @@\n #include \"oidmap.h\"\n #include \"oidset.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n /* Remember to update object flag allocation in object.h */\n /*\ndiff --git a/list-objects.c b/list-objects.c\nindex 943e62e868f..1e5512e1318 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -14,7 +14,7 @@\n #include \"list-objects-filter.h\"\n #include \"list-objects-filter-options.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace.h\"\n #include \"environment.h\"\n \ndiff --git a/log-tree.c b/log-tree.c\nindex 5dd1b63076f..a4d4ab59ca0 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -9,7 +9,7 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"tmp-objdir.h\"\n #include \"commit.h\"\ndiff --git a/mailmap.c b/mailmap.c\nindex f35d20ed7fd..9e2642a043b 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -6,7 +6,7 @@\n #include \"string-list.h\"\n #include \"mailmap.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n char *git_mailmap_file;\ndiff --git a/merge-blobs.c b/merge-blobs.c\nindex 0ad0390fea5..53f36dbc175 100644\n--- a/merge-blobs.c\n+++ b/merge-blobs.c\n@@ -4,7 +4,7 @@\n #include \"merge-ll.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)\n {\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9aedffc546b..981e57698ed 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -27,7 +27,7 @@\n #include \"name-hash.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"revision.h\"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 13750490eff..96e59322a8b 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -8,7 +8,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"read-cache.h\"\n #include \"repository.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 235d29858ff..4abca6cb613 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -12,6 +12,7 @@\n \n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\ndiff --git a/object-name.c b/object-name.c\nindex 91f731373a1..2c751a5352a 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -19,7 +19,7 @@\n #include \"oidtree.h\"\n #include \"packfile.h\"\n #include \"pretty.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/object-store-ll.h b/object-store-ll.h\ndeleted file mode 100644\nindex bb5e8798a1b..00000000000\n--- a/object-store-ll.h\n+++ /dev/null\n@@ -1,517 +0,0 @@\n-#ifndef OBJECT_STORE_LL_H\n-#define OBJECT_STORE_LL_H\n-\n-#include \"hashmap.h\"\n-#include \"object.h\"\n-#include \"list.h\"\n-#include \"thread-utils.h\"\n-#include \"oidset.h\"\n-\n-struct oidmap;\n-struct oidtree;\n-struct strbuf;\n-struct repository;\n-\n-struct object_directory {\n-\tstruct object_directory *next;\n-\n-\t/*\n-\t * Used to store the results of readdir(3) calls when we are OK\n-\t * sacrificing accuracy due to races for speed. That includes\n-\t * object existence with OBJECT_INFO_QUICK, as well as\n-\t * our search for unique abbreviated hashes. Don't use it for tasks\n-\t * requiring greater accuracy!\n-\t *\n-\t * Be sure to call odb_load_loose_cache() before using.\n-\t */\n-\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n-\tstruct oidtree *loose_objects_cache;\n-\n-\t/* Map between object IDs for loose objects. */\n-\tstruct loose_object_map *loose_map;\n-\n-\t/*\n-\t * This is a temporary object store created by the tmp_objdir\n-\t * facility. Disable ref updates since the objects in the store\n-\t * might be discarded on rollback.\n-\t */\n-\tint disable_ref_updates;\n-\n-\t/*\n-\t * This object store is ephemeral, so there is no need to fsync.\n-\t */\n-\tint will_destroy;\n-\n-\t/*\n-\t * Path to the alternative object store. If this is a relative path,\n-\t * it is relative to the current working directory.\n-\t */\n-\tchar *path;\n-};\n-\n-void prepare_alt_odb(struct repository *r);\n-int has_alt_odb(struct repository *r);\n-char *compute_alternate_path(const char *path, struct strbuf *err);\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n-typedef int alt_odb_fn(struct object_directory *, void *);\n-int foreach_alt_odb(alt_odb_fn, void*);\n-typedef void alternate_ref_fn(const struct object_id *oid, void *);\n-void for_each_alternate_ref(alternate_ref_fn, void *);\n-\n-/*\n- * Add the directory to the on-disk alternates file; the new entry will also\n- * take effect in the current process.\n- */\n-void add_to_alternates_file(const char *dir);\n-\n-/*\n- * Add the directory to the in-memory list of alternates (along with any\n- * recursive alternates it points to), but do not modify the on-disk alternates\n- * file.\n- */\n-void add_to_alternates_memory(const char *dir);\n-\n-/*\n- * Replace the current writable object directory with the specified temporary\n- * object directory; returns the former primary object directory.\n- */\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n-\n-/*\n- * Restore a previous ODB replaced by set_temporary_main_odb.\n- */\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n-\n-/*\n- * Populate and return the loose object cache array corresponding to the\n- * given object ID.\n- */\n-struct oidtree *odb_loose_cache(struct object_directory *odb,\n-\t\t\t\t  const struct object_id *oid);\n-\n-/* Empty the loose object cache for the specified object directory. */\n-void odb_clear_loose_cache(struct object_directory *odb);\n-\n-struct packed_git {\n-\tstruct hashmap_entry packmap_ent;\n-\tstruct packed_git *next;\n-\tstruct list_head mru;\n-\tstruct pack_window *windows;\n-\toff_t pack_size;\n-\tconst void *index_data;\n-\tsize_t index_size;\n-\tuint32_t num_objects;\n-\tsize_t crc_offset;\n-\tstruct oidset bad_objects;\n-\tint index_version;\n-\ttime_t mtime;\n-\tint pack_fd;\n-\tint index;              /* for builtin/pack-objects.c */\n-\tunsigned pack_local:1,\n-\t\t pack_keep:1,\n-\t\t pack_keep_in_core:1,\n-\t\t freshened:1,\n-\t\t do_not_close:1,\n-\t\t pack_promisor:1,\n-\t\t multi_pack_index:1,\n-\t\t is_cruft:1;\n-\tunsigned char hash[GIT_MAX_RAWSZ];\n-\tstruct revindex_entry *revindex;\n-\tconst uint32_t *revindex_data;\n-\tconst uint32_t *revindex_map;\n-\tsize_t revindex_size;\n-\t/*\n-\t * mtimes_map points at the beginning of the memory mapped region of\n-\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n-\t * of that .mtimes file\n-\t */\n-\tconst uint32_t *mtimes_map;\n-\tsize_t mtimes_size;\n-\n-\t/* repo denotes the repository this packfile belongs to */\n-\tstruct repository *repo;\n-\n-\t/* something like \".git/objects/pack/xxxxx.pack\" */\n-\tchar pack_name[FLEX_ARRAY]; /* more */\n-};\n-\n-struct multi_pack_index;\n-\n-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n-\t\t\t\t     const struct hashmap_entry *entry,\n-\t\t\t\t     const struct hashmap_entry *entry2,\n-\t\t\t\t     const void *keydata)\n-{\n-\tconst char *key = keydata;\n-\tconst struct packed_git *pg1, *pg2;\n-\n-\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n-\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n-\n-\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n-}\n-\n-struct cached_object_entry;\n-\n-struct raw_object_store {\n-\t/*\n-\t * Set of all object directories; the main directory is first (and\n-\t * cannot be NULL after initialization). Subsequent directories are\n-\t * alternates.\n-\t */\n-\tstruct object_directory *odb;\n-\tstruct object_directory **odb_tail;\n-\tstruct kh_odb_path_map *odb_by_path;\n-\n-\tint loaded_alternates;\n-\n-\t/*\n-\t * A list of alternate object directories loaded from the environment;\n-\t * this should not generally need to be accessed directly, but will\n-\t * populate the \"odb\" list when prepare_alt_odb() is run.\n-\t */\n-\tchar *alternate_db;\n-\n-\t/*\n-\t * Objects that should be substituted by other objects\n-\t * (see git-replace(1)).\n-\t */\n-\tstruct oidmap *replace_map;\n-\tunsigned replace_map_initialized : 1;\n-\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n-\n-\tstruct commit_graph *commit_graph;\n-\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c and midx.c\n-\t */\n-\tstruct multi_pack_index *multi_pack_index;\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c\n-\t */\n-\n-\tstruct packed_git *packed_git;\n-\t/* A most-recently-used ordered version of the packed_git list. */\n-\tstruct list_head packed_git_mru;\n-\n-\tstruct {\n-\t\tstruct packed_git **packs;\n-\t\tunsigned flags;\n-\t} kept_pack_cache;\n-\n-\t/*\n-\t * This is meant to hold a *small* number of objects that you would\n-\t * want repo_read_object_file() to be able to return, but yet you do not want\n-\t * to write them into the object store (e.g. a browse-only\n-\t * application).\n-\t */\n-\tstruct cached_object_entry *cached_objects;\n-\tsize_t cached_object_nr, cached_object_alloc;\n-\n-\t/*\n-\t * A map of packfiles to packed_git structs for tracking which\n-\t * packs have been loaded already.\n-\t */\n-\tstruct hashmap pack_map;\n-\n-\t/*\n-\t * A fast, rough count of the number of objects in the repository.\n-\t * These two fields are not meant for direct access. Use\n-\t * repo_approximate_object_count() instead.\n-\t */\n-\tunsigned long approximate_object_count;\n-\tunsigned approximate_object_count_valid : 1;\n-\n-\t/*\n-\t * Whether packed_git has already been populated with this repository's\n-\t * packs.\n-\t */\n-\tunsigned packed_git_initialized : 1;\n-};\n-\n-struct raw_object_store *raw_object_store_new(void);\n-void raw_object_store_clear(struct raw_object_store *o);\n-\n-/*\n- * Create a temporary file rooted in the object database directory, or\n- * die on failure. The filename is taken from \"pattern\", which should have the\n- * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n- * \"template\" buffer. Returns the open descriptor.\n- */\n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n-\n-/*\n- * Create a pack .keep file named \"name\" (which should generally be the output\n- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n- * error.\n- */\n-int odb_pack_keep(const char *name);\n-\n-/*\n- * Put in `buf` the name of the file in the local object database that\n- * would be used to store a loose object with the specified oid.\n- */\n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid);\n-\n-void *map_loose_object(struct repository *r, const struct object_id *oid,\n-\t\t       unsigned long *size);\n-\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size);\n-\n-/* Read and unpack an object file into memory, write memory to an object file */\n-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n-\n-void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n-\t\t      unsigned long len, enum object_type type,\n-\t\t      struct object_id *oid);\n-\n-/*\n- * Add an object file to the in-memory object store, without writing it\n- * to disk.\n- *\n- * Callers are responsible for calling write_object_file to record the\n- * object in persistent storage before writing any other new objects\n- * that reference it.\n- */\n-int pretend_object_file(struct repository *repo,\n-\t\t\tvoid *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid);\n-\n-struct object_info {\n-\t/* Request */\n-\tenum object_type *typep;\n-\tunsigned long *sizep;\n-\toff_t *disk_sizep;\n-\tstruct object_id *delta_base_oid;\n-\tstruct strbuf *type_name;\n-\tvoid **contentp;\n-\n-\t/* Response */\n-\tenum {\n-\t\tOI_CACHED,\n-\t\tOI_LOOSE,\n-\t\tOI_PACKED,\n-\t\tOI_DBCACHED\n-\t} whence;\n-\tunion {\n-\t\t/*\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } cached;\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } loose;\n-\t\t */\n-\t\tstruct {\n-\t\t\tstruct packed_git *pack;\n-\t\t\toff_t offset;\n-\t\t\tunsigned int is_delta;\n-\t\t} packed;\n-\t} u;\n-};\n-\n-/*\n- * Initializer for a \"struct object_info\" that wants no items. You may\n- * also memset() the memory to all-zeroes.\n- */\n-#define OBJECT_INFO_INIT { 0 }\n-\n-/* Invoke lookup_replace_object() on the given hash */\n-#define OBJECT_INFO_LOOKUP_REPLACE 1\n-/* Allow reading from a loose object file of unknown/bogus type */\n-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n-/* Do not retry packed storage after checking packed and loose storage */\n-#define OBJECT_INFO_QUICK 8\n-/*\n- * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n- * nonzero).\n- */\n-#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n-/*\n- * This is meant for bulk prefetching of missing blobs in a partial\n- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n- */\n-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n-\n-/* Die if object corruption (not just an object being missing) was detected. */\n-#define OBJECT_INFO_DIE_IF_CORRUPT 32\n-\n-int oid_object_info_extended(struct repository *r,\n-\t\t\t     const struct object_id *,\n-\t\t\t     struct object_info *, unsigned flags);\n-\n-/* Retry packed storage after checking packed and loose storage */\n-#define HAS_OBJECT_RECHECK_PACKED 1\n-\n-/*\n- * Returns 1 if the object exists. This function will not lazily fetch objects\n- * in a partial clone.\n- */\n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags);\n-\n-/*\n- * These macros and functions are deprecated. If checking existence for an\n- * object that is likely to be missing and/or whose absence is relatively\n- * inconsequential (or is consequential but the caller is prepared to handle\n- * it), use has_object(), which has better defaults (no lazy fetch in a partial\n- * clone and no rechecking of packed storage). In the unlikely event that a\n- * caller needs to assert existence of an object that it fully expects to\n- * exist, and wants to trigger a lazy fetch in a partial clone, use\n- * oid_object_info_extended() with a NULL struct object_info.\n- *\n- * These functions can be removed once all callers have migrated to\n- * has_object() and/or oid_object_info_extended().\n- */\n-int repo_has_object_file(struct repository *r, const struct object_id *oid);\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags);\n-\n-void assert_oid_type(const struct object_id *oid, enum object_type expect);\n-\n-/*\n- * Enabling the object read lock allows multiple threads to safely call the\n- * following functions in parallel: repo_read_object_file(),\n- * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n- *\n- * obj_read_lock() and obj_read_unlock() may also be used to protect other\n- * section which cannot execute in parallel with object reading. Since the used\n- * lock is a recursive mutex, these sections can even contain calls to object\n- * reading functions. However, beware that in these cases zlib inflation won't\n- * be performed in parallel, losing performance.\n- *\n- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n- * any of its callees end up calling it, this recursive call won't benefit from\n- * parallel inflation.\n- */\n-void enable_obj_read_lock(void);\n-void disable_obj_read_lock(void);\n-\n-extern int obj_read_use_lock;\n-extern pthread_mutex_t obj_read_mutex;\n-\n-static inline void obj_read_lock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_lock(&obj_read_mutex);\n-}\n-\n-static inline void obj_read_unlock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_unlock(&obj_read_mutex);\n-}\n-\n-/*\n- * Iterate over the files in the loose-object parts of the object\n- * directory \"path\", triggering the following callbacks:\n- *\n- *  - loose_object is called for each loose object we find.\n- *\n- *  - loose_cruft is called for any files that do not appear to be\n- *    loose objects. Note that we only look in the loose object\n- *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n- *    \"objects/foobar\" as cruft.\n- *\n- *  - loose_subdir is called for each top-level hashed subdirectory\n- *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n- *    after the objects in the directory are processed.\n- *\n- * Any callback that is NULL will be ignored. Callbacks returning non-zero\n- * will end the iteration.\n- *\n- * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n- * scratch buffer, but restored to its original contents before\n- * the function returns.\n- */\n-typedef int each_loose_object_fn(const struct object_id *oid,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-typedef int each_loose_cruft_fn(const char *basename,\n-\t\t\t\tconst char *path,\n-\t\t\t\tvoid *data);\n-typedef int each_loose_subdir_fn(unsigned int nr,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n-\t\t\t\tstruct strbuf *path,\n-\t\t\t\teach_loose_object_fn obj_cb,\n-\t\t\t\teach_loose_cruft_fn cruft_cb,\n-\t\t\t\teach_loose_subdir_fn subdir_cb,\n-\t\t\t\tvoid *data);\n-int for_each_loose_file_in_objdir(const char *path,\n-\t\t\t\t  each_loose_object_fn obj_cb,\n-\t\t\t\t  each_loose_cruft_fn cruft_cb,\n-\t\t\t\t  each_loose_subdir_fn subdir_cb,\n-\t\t\t\t  void *data);\n-int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n-\t\t\t\t      each_loose_object_fn obj_cb,\n-\t\t\t\t      each_loose_cruft_fn cruft_cb,\n-\t\t\t\t      each_loose_subdir_fn subdir_cb,\n-\t\t\t\t      void *data);\n-\n-/* Flags for for_each_*_object() below. */\n-enum for_each_object_flags {\n-\t/* Iterate only over local objects, not alternates. */\n-\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n-\n-\t/* Only iterate over packs obtained from the promisor remote. */\n-\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n-\n-\t/*\n-\t * Visit objects within a pack in packfile order rather than .idx order\n-\t */\n-\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n-\n-\t/* Only iterate over packs that are not marked as kept in-core. */\n-\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n-\n-\t/* Only iterate over packs that do not have .keep files. */\n-\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n-};\n-\n-/*\n- * Iterate over all accessible loose objects without respect to\n- * reachability. By default, this includes both local and alternate objects.\n- * The order in which objects are visited is unspecified.\n- *\n- * Any flags specific to packs are ignored.\n- */\n-int for_each_loose_object(each_loose_object_fn, void *,\n-\t\t\t  enum for_each_object_flags flags);\n-\n-/*\n- * Iterate over all accessible packed objects without respect to reachability.\n- * By default, this includes both local and alternate packs.\n- *\n- * Note that some objects may appear twice if they are found in multiple packs.\n- * Each pack is visited in an unspecified order. By default, objects within a\n- * pack are visited in pack-idx order (i.e., sorted by oid).\n- */\n-typedef int each_packed_object_fn(const struct object_id *oid,\n-\t\t\t\t  struct packed_git *pack,\n-\t\t\t\t  uint32_t pos,\n-\t\t\t\t  void *data);\n-int for_each_object_in_pack(struct packed_git *p,\n-\t\t\t    each_packed_object_fn, void *data,\n-\t\t\t    enum for_each_object_flags flags);\n-int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n-\t\t\t   void *data, enum for_each_object_flags flags);\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n-\n-#endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nindex 17fa06a86fa..fc48925d230 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -4,9 +4,11 @@\n #include \"abspath.h\"\n #include \"commit-graph.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n+#include \"khash.h\"\n #include \"lockfile.h\"\n #include \"loose.h\"\n #include \"object-file-convert.h\"\n@@ -24,6 +26,9 @@\n #include \"submodule.h\"\n #include \"write-or-die.h\"\n \n+KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n+\tstruct object_directory *, 1, fspathhash, fspatheq)\n+\n /*\n  * This is meant to hold a *small* number of objects that you would\n  * want repo_read_object_file() to be able to return, but yet you do not want\ndiff --git a/object-store.h b/object-store.h\nindex 1b3e3d7d014..46961dc9542 100644\n--- a/object-store.h\n+++ b/object-store.h\n@@ -1,11 +1,517 @@\n #ifndef OBJECT_STORE_H\n #define OBJECT_STORE_H\n \n-#include \"khash.h\"\n-#include \"dir.h\"\n-#include \"object-store-ll.h\"\n+#include \"hashmap.h\"\n+#include \"object.h\"\n+#include \"list.h\"\n+#include \"oidset.h\"\n+#include \"thread-utils.h\"\n \n-KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n-\tstruct object_directory *, 1, fspathhash, fspatheq)\n+struct oidmap;\n+struct oidtree;\n+struct strbuf;\n+struct repository;\n+\n+struct object_directory {\n+\tstruct object_directory *next;\n+\n+\t/*\n+\t * Used to store the results of readdir(3) calls when we are OK\n+\t * sacrificing accuracy due to races for speed. That includes\n+\t * object existence with OBJECT_INFO_QUICK, as well as\n+\t * our search for unique abbreviated hashes. Don't use it for tasks\n+\t * requiring greater accuracy!\n+\t *\n+\t * Be sure to call odb_load_loose_cache() before using.\n+\t */\n+\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n+\tstruct oidtree *loose_objects_cache;\n+\n+\t/* Map between object IDs for loose objects. */\n+\tstruct loose_object_map *loose_map;\n+\n+\t/*\n+\t * This is a temporary object store created by the tmp_objdir\n+\t * facility. Disable ref updates since the objects in the store\n+\t * might be discarded on rollback.\n+\t */\n+\tint disable_ref_updates;\n+\n+\t/*\n+\t * This object store is ephemeral, so there is no need to fsync.\n+\t */\n+\tint will_destroy;\n+\n+\t/*\n+\t * Path to the alternative object store. If this is a relative path,\n+\t * it is relative to the current working directory.\n+\t */\n+\tchar *path;\n+};\n+\n+void prepare_alt_odb(struct repository *r);\n+int has_alt_odb(struct repository *r);\n+char *compute_alternate_path(const char *path, struct strbuf *err);\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n+typedef int alt_odb_fn(struct object_directory *, void *);\n+int foreach_alt_odb(alt_odb_fn, void*);\n+typedef void alternate_ref_fn(const struct object_id *oid, void *);\n+void for_each_alternate_ref(alternate_ref_fn, void *);\n+\n+/*\n+ * Add the directory to the on-disk alternates file; the new entry will also\n+ * take effect in the current process.\n+ */\n+void add_to_alternates_file(const char *dir);\n+\n+/*\n+ * Add the directory to the in-memory list of alternates (along with any\n+ * recursive alternates it points to), but do not modify the on-disk alternates\n+ * file.\n+ */\n+void add_to_alternates_memory(const char *dir);\n+\n+/*\n+ * Replace the current writable object directory with the specified temporary\n+ * object directory; returns the former primary object directory.\n+ */\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n+\n+/*\n+ * Restore a previous ODB replaced by set_temporary_main_odb.\n+ */\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n+\n+/*\n+ * Populate and return the loose object cache array corresponding to the\n+ * given object ID.\n+ */\n+struct oidtree *odb_loose_cache(struct object_directory *odb,\n+\t\t\t\t  const struct object_id *oid);\n+\n+/* Empty the loose object cache for the specified object directory. */\n+void odb_clear_loose_cache(struct object_directory *odb);\n+\n+struct packed_git {\n+\tstruct hashmap_entry packmap_ent;\n+\tstruct packed_git *next;\n+\tstruct list_head mru;\n+\tstruct pack_window *windows;\n+\toff_t pack_size;\n+\tconst void *index_data;\n+\tsize_t index_size;\n+\tuint32_t num_objects;\n+\tsize_t crc_offset;\n+\tstruct oidset bad_objects;\n+\tint index_version;\n+\ttime_t mtime;\n+\tint pack_fd;\n+\tint index;              /* for builtin/pack-objects.c */\n+\tunsigned pack_local:1,\n+\t\t pack_keep:1,\n+\t\t pack_keep_in_core:1,\n+\t\t freshened:1,\n+\t\t do_not_close:1,\n+\t\t pack_promisor:1,\n+\t\t multi_pack_index:1,\n+\t\t is_cruft:1;\n+\tunsigned char hash[GIT_MAX_RAWSZ];\n+\tstruct revindex_entry *revindex;\n+\tconst uint32_t *revindex_data;\n+\tconst uint32_t *revindex_map;\n+\tsize_t revindex_size;\n+\t/*\n+\t * mtimes_map points at the beginning of the memory mapped region of\n+\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n+\t * of that .mtimes file\n+\t */\n+\tconst uint32_t *mtimes_map;\n+\tsize_t mtimes_size;\n+\n+\t/* repo denotes the repository this packfile belongs to */\n+\tstruct repository *repo;\n+\n+\t/* something like \".git/objects/pack/xxxxx.pack\" */\n+\tchar pack_name[FLEX_ARRAY]; /* more */\n+};\n+\n+struct multi_pack_index;\n+\n+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n+\t\t\t\t     const struct hashmap_entry *entry,\n+\t\t\t\t     const struct hashmap_entry *entry2,\n+\t\t\t\t     const void *keydata)\n+{\n+\tconst char *key = keydata;\n+\tconst struct packed_git *pg1, *pg2;\n+\n+\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n+\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n+\n+\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n+}\n+\n+struct cached_object_entry;\n+\n+struct raw_object_store {\n+\t/*\n+\t * Set of all object directories; the main directory is first (and\n+\t * cannot be NULL after initialization). Subsequent directories are\n+\t * alternates.\n+\t */\n+\tstruct object_directory *odb;\n+\tstruct object_directory **odb_tail;\n+\tstruct kh_odb_path_map *odb_by_path;\n+\n+\tint loaded_alternates;\n+\n+\t/*\n+\t * A list of alternate object directories loaded from the environment;\n+\t * this should not generally need to be accessed directly, but will\n+\t * populate the \"odb\" list when prepare_alt_odb() is run.\n+\t */\n+\tchar *alternate_db;\n+\n+\t/*\n+\t * Objects that should be substituted by other objects\n+\t * (see git-replace(1)).\n+\t */\n+\tstruct oidmap *replace_map;\n+\tunsigned replace_map_initialized : 1;\n+\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n+\n+\tstruct commit_graph *commit_graph;\n+\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c and midx.c\n+\t */\n+\tstruct multi_pack_index *multi_pack_index;\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c\n+\t */\n+\n+\tstruct packed_git *packed_git;\n+\t/* A most-recently-used ordered version of the packed_git list. */\n+\tstruct list_head packed_git_mru;\n+\n+\tstruct {\n+\t\tstruct packed_git **packs;\n+\t\tunsigned flags;\n+\t} kept_pack_cache;\n+\n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n+\t/*\n+\t * A map of packfiles to packed_git structs for tracking which\n+\t * packs have been loaded already.\n+\t */\n+\tstruct hashmap pack_map;\n+\n+\t/*\n+\t * A fast, rough count of the number of objects in the repository.\n+\t * These two fields are not meant for direct access. Use\n+\t * repo_approximate_object_count() instead.\n+\t */\n+\tunsigned long approximate_object_count;\n+\tunsigned approximate_object_count_valid : 1;\n+\n+\t/*\n+\t * Whether packed_git has already been populated with this repository's\n+\t * packs.\n+\t */\n+\tunsigned packed_git_initialized : 1;\n+};\n+\n+struct raw_object_store *raw_object_store_new(void);\n+void raw_object_store_clear(struct raw_object_store *o);\n+\n+/*\n+ * Create a temporary file rooted in the object database directory, or\n+ * die on failure. The filename is taken from \"pattern\", which should have the\n+ * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n+ * \"template\" buffer. Returns the open descriptor.\n+ */\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n+\n+/*\n+ * Create a pack .keep file named \"name\" (which should generally be the output\n+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n+ * error.\n+ */\n+int odb_pack_keep(const char *name);\n+\n+/*\n+ * Put in `buf` the name of the file in the local object database that\n+ * would be used to store a loose object with the specified oid.\n+ */\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid);\n+\n+void *map_loose_object(struct repository *r, const struct object_id *oid,\n+\t\t       unsigned long *size);\n+\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size);\n+\n+/* Read and unpack an object file into memory, write memory to an object file */\n+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n+\n+void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n+\t\t      unsigned long len, enum object_type type,\n+\t\t      struct object_id *oid);\n+\n+/*\n+ * Add an object file to the in-memory object store, without writing it\n+ * to disk.\n+ *\n+ * Callers are responsible for calling write_object_file to record the\n+ * object in persistent storage before writing any other new objects\n+ * that reference it.\n+ */\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid);\n+\n+struct object_info {\n+\t/* Request */\n+\tenum object_type *typep;\n+\tunsigned long *sizep;\n+\toff_t *disk_sizep;\n+\tstruct object_id *delta_base_oid;\n+\tstruct strbuf *type_name;\n+\tvoid **contentp;\n+\n+\t/* Response */\n+\tenum {\n+\t\tOI_CACHED,\n+\t\tOI_LOOSE,\n+\t\tOI_PACKED,\n+\t\tOI_DBCACHED\n+\t} whence;\n+\tunion {\n+\t\t/*\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } cached;\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } loose;\n+\t\t */\n+\t\tstruct {\n+\t\t\tstruct packed_git *pack;\n+\t\t\toff_t offset;\n+\t\t\tunsigned int is_delta;\n+\t\t} packed;\n+\t} u;\n+};\n+\n+/*\n+ * Initializer for a \"struct object_info\" that wants no items. You may\n+ * also memset() the memory to all-zeroes.\n+ */\n+#define OBJECT_INFO_INIT { 0 }\n+\n+/* Invoke lookup_replace_object() on the given hash */\n+#define OBJECT_INFO_LOOKUP_REPLACE 1\n+/* Allow reading from a loose object file of unknown/bogus type */\n+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n+/* Do not retry packed storage after checking packed and loose storage */\n+#define OBJECT_INFO_QUICK 8\n+/*\n+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n+ * nonzero).\n+ */\n+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n+/*\n+ * This is meant for bulk prefetching of missing blobs in a partial\n+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n+ */\n+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n+\n+/* Die if object corruption (not just an object being missing) was detected. */\n+#define OBJECT_INFO_DIE_IF_CORRUPT 32\n+\n+int oid_object_info_extended(struct repository *r,\n+\t\t\t     const struct object_id *,\n+\t\t\t     struct object_info *, unsigned flags);\n+\n+/* Retry packed storage after checking packed and loose storage */\n+#define HAS_OBJECT_RECHECK_PACKED 1\n+\n+/*\n+ * Returns 1 if the object exists. This function will not lazily fetch objects\n+ * in a partial clone.\n+ */\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags);\n+\n+/*\n+ * These macros and functions are deprecated. If checking existence for an\n+ * object that is likely to be missing and/or whose absence is relatively\n+ * inconsequential (or is consequential but the caller is prepared to handle\n+ * it), use has_object(), which has better defaults (no lazy fetch in a partial\n+ * clone and no rechecking of packed storage). In the unlikely event that a\n+ * caller needs to assert existence of an object that it fully expects to\n+ * exist, and wants to trigger a lazy fetch in a partial clone, use\n+ * oid_object_info_extended() with a NULL struct object_info.\n+ *\n+ * These functions can be removed once all callers have migrated to\n+ * has_object() and/or oid_object_info_extended().\n+ */\n+int repo_has_object_file(struct repository *r, const struct object_id *oid);\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags);\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect);\n+\n+/*\n+ * Enabling the object read lock allows multiple threads to safely call the\n+ * following functions in parallel: repo_read_object_file(),\n+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n+ *\n+ * obj_read_lock() and obj_read_unlock() may also be used to protect other\n+ * section which cannot execute in parallel with object reading. Since the used\n+ * lock is a recursive mutex, these sections can even contain calls to object\n+ * reading functions. However, beware that in these cases zlib inflation won't\n+ * be performed in parallel, losing performance.\n+ *\n+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n+ * any of its callees end up calling it, this recursive call won't benefit from\n+ * parallel inflation.\n+ */\n+void enable_obj_read_lock(void);\n+void disable_obj_read_lock(void);\n+\n+extern int obj_read_use_lock;\n+extern pthread_mutex_t obj_read_mutex;\n+\n+static inline void obj_read_lock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_lock(&obj_read_mutex);\n+}\n+\n+static inline void obj_read_unlock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_unlock(&obj_read_mutex);\n+}\n+\n+/*\n+ * Iterate over the files in the loose-object parts of the object\n+ * directory \"path\", triggering the following callbacks:\n+ *\n+ *  - loose_object is called for each loose object we find.\n+ *\n+ *  - loose_cruft is called for any files that do not appear to be\n+ *    loose objects. Note that we only look in the loose object\n+ *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n+ *    \"objects/foobar\" as cruft.\n+ *\n+ *  - loose_subdir is called for each top-level hashed subdirectory\n+ *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n+ *    after the objects in the directory are processed.\n+ *\n+ * Any callback that is NULL will be ignored. Callbacks returning non-zero\n+ * will end the iteration.\n+ *\n+ * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n+ * scratch buffer, but restored to its original contents before\n+ * the function returns.\n+ */\n+typedef int each_loose_object_fn(const struct object_id *oid,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+typedef int each_loose_cruft_fn(const char *basename,\n+\t\t\t\tconst char *path,\n+\t\t\t\tvoid *data);\n+typedef int each_loose_subdir_fn(unsigned int nr,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n+\t\t\t\tstruct strbuf *path,\n+\t\t\t\teach_loose_object_fn obj_cb,\n+\t\t\t\teach_loose_cruft_fn cruft_cb,\n+\t\t\t\teach_loose_subdir_fn subdir_cb,\n+\t\t\t\tvoid *data);\n+int for_each_loose_file_in_objdir(const char *path,\n+\t\t\t\t  each_loose_object_fn obj_cb,\n+\t\t\t\t  each_loose_cruft_fn cruft_cb,\n+\t\t\t\t  each_loose_subdir_fn subdir_cb,\n+\t\t\t\t  void *data);\n+int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n+\t\t\t\t      each_loose_object_fn obj_cb,\n+\t\t\t\t      each_loose_cruft_fn cruft_cb,\n+\t\t\t\t      each_loose_subdir_fn subdir_cb,\n+\t\t\t\t      void *data);\n+\n+/* Flags for for_each_*_object() below. */\n+enum for_each_object_flags {\n+\t/* Iterate only over local objects, not alternates. */\n+\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n+\n+\t/* Only iterate over packs obtained from the promisor remote. */\n+\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n+\n+\t/*\n+\t * Visit objects within a pack in packfile order rather than .idx order\n+\t */\n+\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n+\n+\t/* Only iterate over packs that are not marked as kept in-core. */\n+\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n+\n+\t/* Only iterate over packs that do not have .keep files. */\n+\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n+};\n+\n+/*\n+ * Iterate over all accessible loose objects without respect to\n+ * reachability. By default, this includes both local and alternate objects.\n+ * The order in which objects are visited is unspecified.\n+ *\n+ * Any flags specific to packs are ignored.\n+ */\n+int for_each_loose_object(each_loose_object_fn, void *,\n+\t\t\t  enum for_each_object_flags flags);\n+\n+/*\n+ * Iterate over all accessible packed objects without respect to reachability.\n+ * By default, this includes both local and alternate packs.\n+ *\n+ * Note that some objects may appear twice if they are found in multiple packs.\n+ * Each pack is visited in an unspecified order. By default, objects within a\n+ * pack are visited in pack-idx order (i.e., sorted by oid).\n+ */\n+typedef int each_packed_object_fn(const struct object_id *oid,\n+\t\t\t\t  struct packed_git *pack,\n+\t\t\t\t  uint32_t pos,\n+\t\t\t\t  void *data);\n+int for_each_object_in_pack(struct packed_git *p,\n+\t\t\t    each_packed_object_fn, void *data,\n+\t\t\t    enum for_each_object_flags flags);\n+int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n+\t\t\t   void *data, enum for_each_object_flags flags);\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n \n #endif /* OBJECT_STORE_H */\ndiff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c\nindex 3e190214d14..609a343ee3e 100644\n--- a/oss-fuzz/fuzz-pack-idx.c\n+++ b/oss-fuzz/fuzz-pack-idx.c\n@@ -1,5 +1,5 @@\n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n \n int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 6a97b52b36d..4c7f7985e78 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -4,7 +4,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 0dbd7c4ffe1..fb68d2ae637 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,7 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\n #include \"config.h\"\ndiff --git a/pack-check.c b/pack-check.c\nindex 95dcbbe9852..874897d6cba 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -8,7 +8,7 @@\n #include \"progress.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct idx_entry {\n \toff_t                offset;\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex bcea28e521d..20900ca88d3 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n \ndiff --git a/pack-objects.h b/pack-objects.h\nindex d73e3843c92..d1c4ae7f9b6 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -1,7 +1,7 @@\n #ifndef PACK_OBJECTS_H\n #define PACK_OBJECTS_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"thread-utils.h\"\n #include \"pack.h\"\n \ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 1ee7b49e206..37ad9c79268 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\ndiff --git a/packfile.c b/packfile.c\nindex 9d09f8bc726..d91016f1c7f 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -19,7 +19,7 @@\n #include \"tree-walk.h\"\n #include \"tree.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"midx.h\"\n #include \"commit-graph.h\"\n #include \"pack-revindex.h\"\ndiff --git a/path.c b/path.c\nindex 910756c8b32..1f7a014322c 100644\n--- a/path.c\n+++ b/path.c\n@@ -15,7 +15,7 @@\n #include \"submodule-config.h\"\n #include \"path.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"lockfile.h\"\n #include \"exec-cmd.h\"\n \ndiff --git a/promisor-remote.c b/promisor-remote.c\nindex 5801ebfd9b2..9d058586dfa 100644\n--- a/promisor-remote.c\n+++ b/promisor-remote.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"config.h\"\n #include \"trace2.h\"\ndiff --git a/protocol-caps.c b/protocol-caps.c\nindex 855f279c2f7..9b8db37a210 100644\n--- a/protocol-caps.c\n+++ b/protocol-caps.c\n@@ -6,7 +6,7 @@\n #include \"hash.h\"\n #include \"hex.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"string-list.h\"\n #include \"strbuf.h\"\ndiff --git a/prune-packed.c b/prune-packed.c\nindex 7dad2fc0c16..c1d95a519d7 100644\n--- a/prune-packed.c\n+++ b/prune-packed.c\n@@ -2,7 +2,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"progress.h\"\n #include \"prune-packed.h\"\ndiff --git a/reachable.c b/reachable.c\nindex 1b26b9b1d76..16e23a38037 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -14,7 +14,7 @@\n #include \"list-objects.h\"\n #include \"packfile.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"pack-mtimes.h\"\n #include \"config.h\"\ndiff --git a/read-cache.c b/read-cache.c\nindex 9909b56902f..9aef59cab4c 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -22,7 +22,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"tree.h\"\n #include \"commit.h\"\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 6da8d4c03b6..7a274633cfc 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"wildmatch.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/reflog.c b/reflog.c\nindex 1b5f031f6d7..60834a124d9 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"reflog.h\"\n #include \"refs.h\"\n #include \"revision.h\"\ndiff --git a/refs.c b/refs.c\nindex 1208f86629b..14f25c499cd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"hook.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"path.h\"\n #include \"submodule.h\"\ndiff --git a/remote.c b/remote.c\nindex e609cf5c56a..7fc657d98b8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"commit.h\"\n #include \"diff.h\"\ndiff --git a/replace-object.c b/replace-object.c\nindex 9a3cdd809a9..7b8a09b5cb4 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -2,7 +2,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"oidmap.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/replace-object.h b/replace-object.h\nindex 66c41b938b4..ba478eb30c4 100644\n--- a/replace-object.h\n+++ b/replace-object.h\n@@ -3,7 +3,7 @@\n \n #include \"oidmap.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct replace_object {\n \tstruct oidmap_entry original;\ndiff --git a/repository.c b/repository.c\nindex 6cbaf2e3daa..9b3d6665fc6 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\ndiff --git a/rerere.c b/rerere.c\nindex 740e8ad1a0b..28e59c42380 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -18,7 +18,7 @@\n #include \"path.h\"\n #include \"pathspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strmap.h\"\n \n #define RESOLVED 0\ndiff --git a/revision.c b/revision.c\nindex b536c4a29ad..352e18b1f97 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -8,7 +8,7 @@\n #include \"hex.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"tag.h\"\n #include \"blob.h\"\ndiff --git a/send-pack.c b/send-pack.c\nindex 856a65d5f5a..5005689cb55 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"run-command.h\"\ndiff --git a/sequencer.c b/sequencer.c\nindex c112d2e1c43..925cfa1af68 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -13,7 +13,7 @@\n #include \"dir.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"pager.h\"\n #include \"commit.h\"\ndiff --git a/server-info.c b/server-info.c\nindex 1ca0e00d51e..3048e5ee407 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -11,7 +11,7 @@\n #include \"packfile.h\"\n #include \"path.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"server-info.h\"\n #include \"strbuf.h\"\n #include \"tempfile.h\"\ndiff --git a/shallow.c b/shallow.c\nindex 06c3266a3e0..2f82ebd6e3f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -5,7 +5,7 @@\n #include \"repository.h\"\n #include \"tempfile.h\"\n #include \"lockfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"pkt-line.h\"\ndiff --git a/streaming.c b/streaming.c\nindex 018b794d252..127d6b5d6ac 100644\n--- a/streaming.c\n+++ b/streaming.c\n@@ -10,7 +10,7 @@\n #include \"streaming.h\"\n #include \"repository.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"packfile.h\"\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex d82b404b73e..8630e27947d 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -13,7 +13,7 @@\n #include \"submodule.h\"\n #include \"strbuf.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/submodule.c b/submodule.c\nindex 0821507ecaa..3e52ef3b036 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -27,7 +27,7 @@\n #include \"parse-options.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n #include \"read-cache-ll.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c\nindex f8f9afbb5b1..50f5941bff7 100644\n--- a/t/helper/test-pack-mtimes.c\n+++ b/t/helper/test-pack-mtimes.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"strbuf.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pack-mtimes.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c\nindex a1af9710c31..34f1aee5581 100644\n--- a/t/helper/test-partial-clone.c\n+++ b/t/helper/test-partial-clone.c\n@@ -1,7 +1,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n /*\ndiff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c\nindex 811dde1cb3c..8b413b644be 100644\n--- a/t/helper/test-read-graph.c\n+++ b/t/helper/test-read-graph.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"commit-graph.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"bloom.h\"\n #include \"setup.h\"\n \ndiff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c\nindex fc632369618..ac81390899a 100644\n--- a/t/helper/test-read-midx.c\n+++ b/t/helper/test-read-midx.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"midx.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"packfile.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 2ff67c067ac..4cfc7c90b59 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -5,7 +5,7 @@\n #include \"refs.h\"\n #include \"setup.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\ndiff --git a/tag.c b/tag.c\nindex 8d9e9e29304..05be39067cf 100644\n--- a/tag.c\n+++ b/tag.c\n@@ -5,7 +5,7 @@\n #include \"environment.h\"\n #include \"tag.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"blob.h\"\ndiff --git a/tmp-objdir.c b/tmp-objdir.c\nindex 31d16a4c2c5..c38fbeb5e8a 100644\n--- a/tmp-objdir.c\n+++ b/tmp-objdir.c\n@@ -10,7 +10,7 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n #include \"quote.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n struct tmp_objdir {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a0333979656..90655d52378 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -6,7 +6,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace2.h\"\n #include \"tree.h\"\n #include \"pathspec.h\"\ndiff --git a/tree.c b/tree.c\nindex ad86ad1ba99..b85f56267fb 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"tree.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"alloc.h\"\n #include \"tree-walk.h\"\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex cf5b73c84be..471837f0329 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -26,7 +26,7 @@\n #include \"symlinks.h\"\n #include \"trace2.h\"\n #include \"fsmonitor.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 02ce6336028..30e4630f3a1 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -10,7 +10,7 @@\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/walker.c b/walker.c\nindex 1cf3da02193..4fedc19f346 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"walker.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"strbuf.h\"\n #include \"tree.h\"\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 77712811ff1..1edcd319e6e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"config.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"xdiff-interface.h\"\n #include \"xdiff/xtypes.h\"\n\n-- \n2.49.0.682.gc9b6a7b2b0.dirty\n\n"},{"id":"515905","messageId":"xmqqsemiteot.fsf@gitster.g","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"Re: [PATCH 0/9] Split up \"object-file.c\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-08T23:29:38Z","receivedAt":"2025-04-08T23:29:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\n> with ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\n> branch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\n> into it.\n\nAs 9442b1c919a is not public, this description lacks a way to verify\nthe result I attempted to reproduce.  ps/object-wo-the-repository I\nhave ends with 7d70b29c (hash: stop depending on `the_repository` in\n`null_oid()`, 2025-03-10) and it hasn't moved for a while, so\nhopefully we are in agreement ;-)\n\nThere were a few unpleasant interactions with other topics in\nflight; please check the conflict resolution I made.\n\nWill queue.\n"},{"id":"515915","messageId":"CABPp-BFpU5iLUN6Fh_+UG2Y593TWp4E+C_QQxLg6b=Cb-30F6A@mail.gmail.com","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-1-f1fd50191143@pks.im","subject":"Re: [PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-04-09T14:36:47Z","receivedAt":"2025-04-09T14:37:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Apr 8, 2025 at 3:37 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> The `safe_create_leading_directories()` function and its relatives\n\nHow is mkdir_in_gitdir() a relative of safe_create_leading_directories()?\n\nI assumed the relation was \"called by\", but there is no such\nrelationship.  The rest of the patch looked fine, but I was puzzled\nfor a while trying to figure out what this relationship is.\n\n> are\n> located in \"object-file.c\", which is not a good fit as they provide\n> generic functionality not related to objects at all. Move them into\n> \"dir.c\".\n\nMakes sense.\n\n\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/bugreport.c                |   2 +-\n>  builtin/credential-cache--daemon.c |   2 +-\n>  builtin/diagnose.c                 |   2 +-\n>  builtin/fsck.c                     |   1 +\n>  builtin/gc.c                       |   2 +-\n>  builtin/init-db.c                  |   2 +-\n>  builtin/log.c                      |   2 +-\n>  commit-graph.c                     |   1 +\n>  dir.c                              | 107 ++++++++++++++++++++++++++++++++++++-\n>  dir.h                              |  35 ++++++++++++\n>  midx-write.c                       |   1 +\n>  object-file.c                      | 106 ------------------------------------\n>  object-file.h                      |  35 ------------\n>  13 files changed, 150 insertions(+), 148 deletions(-)\n>\n> diff --git a/builtin/bugreport.c b/builtin/bugreport.c\n> index 66d64bfd5ae..d07fa91c247 100644\n> --- a/builtin/bugreport.c\n> +++ b/builtin/bugreport.c\n> @@ -1,6 +1,7 @@\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n> +#include \"dir.h\"\n>  #include \"editor.h\"\n>  #include \"gettext.h\"\n>  #include \"parse-options.h\"\n> @@ -10,7 +11,6 @@\n>  #include \"hook.h\"\n>  #include \"hook-list.h\"\n>  #include \"diagnose.h\"\n> -#include \"object-file.h\"\n>  #include \"setup.h\"\n>  #include \"version.h\"\n>\n> diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c\n> index e707618e743..80d29b4f5c0 100644\n> --- a/builtin/credential-cache--daemon.c\n> +++ b/builtin/credential-cache--daemon.c\n> @@ -1,8 +1,8 @@\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n> +#include \"dir.h\"\n>  #include \"gettext.h\"\n> -#include \"object-file.h\"\n>  #include \"parse-options.h\"\n>\n>  #ifndef NO_UNIX_SOCKETS\n> diff --git a/builtin/diagnose.c b/builtin/diagnose.c\n> index 33c39bd5981..d5dadd6a48b 100644\n> --- a/builtin/diagnose.c\n> +++ b/builtin/diagnose.c\n> @@ -2,8 +2,8 @@\n>\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n> +#include \"dir.h\"\n>  #include \"gettext.h\"\n> -#include \"object-file.h\"\n>  #include \"parse-options.h\"\n>  #include \"diagnose.h\"\n>\n> diff --git a/builtin/fsck.c b/builtin/fsck.c\n> index 9c8a6d6a8df..32d40d8f9fc 100644\n> --- a/builtin/fsck.c\n> +++ b/builtin/fsck.c\n> @@ -1,5 +1,6 @@\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  #include \"builtin.h\"\n> +#include \"dir.h\"\n>  #include \"gettext.h\"\n>  #include \"hex.h\"\n>  #include \"config.h\"\n> diff --git a/builtin/gc.c b/builtin/gc.c\n> index 99431fd4674..b069629676c 100644\n> --- a/builtin/gc.c\n> +++ b/builtin/gc.c\n> @@ -16,6 +16,7 @@\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n>  #include \"date.h\"\n> +#include \"dir.h\"\n>  #include \"environment.h\"\n>  #include \"hex.h\"\n>  #include \"config.h\"\n> @@ -28,7 +29,6 @@\n>  #include \"commit.h\"\n>  #include \"commit-graph.h\"\n>  #include \"packfile.h\"\n> -#include \"object-file.h\"\n>  #include \"object-store-ll.h\"\n>  #include \"pack.h\"\n>  #include \"pack-objects.h\"\n> diff --git a/builtin/init-db.c b/builtin/init-db.c\n> index 196dccdd77a..39730c1b0ce 100644\n> --- a/builtin/init-db.c\n> +++ b/builtin/init-db.c\n> @@ -6,9 +6,9 @@\n>  #define USE_THE_REPOSITORY_VARIABLE\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n> +#include \"dir.h\"\n>  #include \"environment.h\"\n>  #include \"gettext.h\"\n> -#include \"object-file.h\"\n>  #include \"parse-options.h\"\n>  #include \"path.h\"\n>  #include \"refs.h\"\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 0d4c579dad7..06ffaa93e86 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -10,11 +10,11 @@\n>  #include \"builtin.h\"\n>  #include \"abspath.h\"\n>  #include \"config.h\"\n> +#include \"dir.h\"\n>  #include \"environment.h\"\n>  #include \"gettext.h\"\n>  #include \"hex.h\"\n>  #include \"refs.h\"\n> -#include \"object-file.h\"\n>  #include \"object-name.h\"\n>  #include \"object-store-ll.h\"\n>  #include \"pager.h\"\n> diff --git a/commit-graph.c b/commit-graph.c\n> index 8286d5dda24..3fae20dc21b 100644\n> --- a/commit-graph.c\n> +++ b/commit-graph.c\n> @@ -4,6 +4,7 @@\n>  #include \"git-compat-util.h\"\n>  #include \"config.h\"\n>  #include \"csum-file.h\"\n> +#include \"dir.h\"\n>  #include \"gettext.h\"\n>  #include \"hex.h\"\n>  #include \"lockfile.h\"\n> diff --git a/dir.c b/dir.c\n> index 28b0e03feb4..16ae3b5169d 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -17,7 +17,6 @@\n>  #include \"environment.h\"\n>  #include \"gettext.h\"\n>  #include \"name-hash.h\"\n> -#include \"object-file.h\"\n>  #include \"object-store-ll.h\"\n>  #include \"path.h\"\n>  #include \"refs.h\"\n> @@ -4132,3 +4131,109 @@ int path_match_flags(const char *const str, const enum path_match_flags flags)\n>                 return is_xplatform_dir_sep(*p);\n>         BUG(\"unreachable\");\n>  }\n> +\n> +int mkdir_in_gitdir(const char *path)\n> +{\n> +       if (mkdir(path, 0777)) {\n> +               int saved_errno = errno;\n> +               struct stat st;\n> +               struct strbuf sb = STRBUF_INIT;\n> +\n> +               if (errno != EEXIST)\n> +                       return -1;\n> +               /*\n> +                * Are we looking at a path in a symlinked worktree\n> +                * whose original repository does not yet have it?\n> +                * e.g. .git/rr-cache pointing at its original\n> +                * repository in which the user hasn't performed any\n> +                * conflict resolution yet?\n> +                */\n> +               if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n> +                   strbuf_readlink(&sb, path, st.st_size) ||\n> +                   !is_absolute_path(sb.buf) ||\n> +                   mkdir(sb.buf, 0777)) {\n> +                       strbuf_release(&sb);\n> +                       errno = saved_errno;\n> +                       return -1;\n> +               }\n> +               strbuf_release(&sb);\n> +       }\n> +       return adjust_shared_perm(the_repository, path);\n> +}\n> +\n> +static enum scld_error safe_create_leading_directories_1(char *path, int share)\n> +{\n> +       char *next_component = path + offset_1st_component(path);\n> +       enum scld_error ret = SCLD_OK;\n> +\n> +       while (ret == SCLD_OK && next_component) {\n> +               struct stat st;\n> +               char *slash = next_component, slash_character;\n> +\n> +               while (*slash && !is_dir_sep(*slash))\n> +                       slash++;\n> +\n> +               if (!*slash)\n> +                       break;\n> +\n> +               next_component = slash + 1;\n> +               while (is_dir_sep(*next_component))\n> +                       next_component++;\n> +               if (!*next_component)\n> +                       break;\n> +\n> +               slash_character = *slash;\n> +               *slash = '\\0';\n> +               if (!stat(path, &st)) {\n> +                       /* path exists */\n> +                       if (!S_ISDIR(st.st_mode)) {\n> +                               errno = ENOTDIR;\n> +                               ret = SCLD_EXISTS;\n> +                       }\n> +               } else if (mkdir(path, 0777)) {\n> +                       if (errno == EEXIST &&\n> +                           !stat(path, &st) && S_ISDIR(st.st_mode))\n> +                               ; /* somebody created it since we checked */\n> +                       else if (errno == ENOENT)\n> +                               /*\n> +                                * Either mkdir() failed because\n> +                                * somebody just pruned the containing\n> +                                * directory, or stat() failed because\n> +                                * the file that was in our way was\n> +                                * just removed.  Either way, inform\n> +                                * the caller that it might be worth\n> +                                * trying again:\n> +                                */\n> +                               ret = SCLD_VANISHED;\n> +                       else\n> +                               ret = SCLD_FAILED;\n> +               } else if (share && adjust_shared_perm(the_repository, path)) {\n> +                       ret = SCLD_PERMS;\n> +               }\n> +               *slash = slash_character;\n> +       }\n> +       return ret;\n> +}\n> +\n> +enum scld_error safe_create_leading_directories(char *path)\n> +{\n> +       return safe_create_leading_directories_1(path, 1);\n> +}\n> +\n> +enum scld_error safe_create_leading_directories_no_share(char *path)\n> +{\n> +       return safe_create_leading_directories_1(path, 0);\n> +}\n> +\n> +enum scld_error safe_create_leading_directories_const(const char *path)\n> +{\n> +       int save_errno;\n> +       /* path points to cache entries, so xstrdup before messing with it */\n> +       char *buf = xstrdup(path);\n> +       enum scld_error result = safe_create_leading_directories(buf);\n> +\n> +       save_errno = errno;\n> +       free(buf);\n> +       errno = save_errno;\n> +       return result;\n> +}\n> diff --git a/dir.h b/dir.h\n> index d7e71aa8daa..02c1f9420b0 100644\n> --- a/dir.h\n> +++ b/dir.h\n> @@ -676,4 +676,39 @@ static inline int starts_with_dot_dot_slash_native(const char *const path)\n>         return path_match_flags(path, what | PATH_MATCH_NATIVE);\n>  }\n>\n> +/*\n> + * Create the directory containing the named path, using care to be\n> + * somewhat safe against races. Return one of the scld_error values to\n> + * indicate success/failure. On error, set errno to describe the\n> + * problem.\n> + *\n> + * SCLD_VANISHED indicates that one of the ancestor directories of the\n> + * path existed at one point during the function call and then\n> + * suddenly vanished, probably because another process pruned the\n> + * directory while we were working.  To be robust against this kind of\n> + * race, callers might want to try invoking the function again when it\n> + * returns SCLD_VANISHED.\n> + *\n> + * safe_create_leading_directories() temporarily changes path while it\n> + * is working but restores it before returning.\n> + * safe_create_leading_directories_const() doesn't modify path, even\n> + * temporarily. Both these variants adjust the permissions of the\n> + * created directories to honor core.sharedRepository, so they are best\n> + * suited for files inside the git dir. For working tree files, use\n> + * safe_create_leading_directories_no_share() instead, as it ignores\n> + * the core.sharedRepository setting.\n> + */\n> +enum scld_error {\n> +       SCLD_OK = 0,\n> +       SCLD_FAILED = -1,\n> +       SCLD_PERMS = -2,\n> +       SCLD_EXISTS = -3,\n> +       SCLD_VANISHED = -4\n> +};\n> +enum scld_error safe_create_leading_directories(char *path);\n> +enum scld_error safe_create_leading_directories_const(const char *path);\n> +enum scld_error safe_create_leading_directories_no_share(char *path);\n> +\n> +int mkdir_in_gitdir(const char *path);\n> +\n>  #endif\n> diff --git a/midx-write.c b/midx-write.c\n> index a628ac24dcb..e01a867c583 100644\n> --- a/midx-write.c\n> +++ b/midx-write.c\n> @@ -3,6 +3,7 @@\n>  #include \"git-compat-util.h\"\n>  #include \"abspath.h\"\n>  #include \"config.h\"\n> +#include \"dir.h\"\n>  #include \"hex.h\"\n>  #include \"lockfile.h\"\n>  #include \"packfile.h\"\n> diff --git a/object-file.c b/object-file.c\n> index 772c311f188..23b2c8560be 100644\n> --- a/object-file.c\n> +++ b/object-file.c\n> @@ -91,112 +91,6 @@ static int get_conv_flags(unsigned flags)\n>  }\n>\n>\n> -int mkdir_in_gitdir(const char *path)\n> -{\n> -       if (mkdir(path, 0777)) {\n> -               int saved_errno = errno;\n> -               struct stat st;\n> -               struct strbuf sb = STRBUF_INIT;\n> -\n> -               if (errno != EEXIST)\n> -                       return -1;\n> -               /*\n> -                * Are we looking at a path in a symlinked worktree\n> -                * whose original repository does not yet have it?\n> -                * e.g. .git/rr-cache pointing at its original\n> -                * repository in which the user hasn't performed any\n> -                * conflict resolution yet?\n> -                */\n> -               if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n> -                   strbuf_readlink(&sb, path, st.st_size) ||\n> -                   !is_absolute_path(sb.buf) ||\n> -                   mkdir(sb.buf, 0777)) {\n> -                       strbuf_release(&sb);\n> -                       errno = saved_errno;\n> -                       return -1;\n> -               }\n> -               strbuf_release(&sb);\n> -       }\n> -       return adjust_shared_perm(the_repository, path);\n> -}\n> -\n> -static enum scld_error safe_create_leading_directories_1(char *path, int share)\n> -{\n> -       char *next_component = path + offset_1st_component(path);\n> -       enum scld_error ret = SCLD_OK;\n> -\n> -       while (ret == SCLD_OK && next_component) {\n> -               struct stat st;\n> -               char *slash = next_component, slash_character;\n> -\n> -               while (*slash && !is_dir_sep(*slash))\n> -                       slash++;\n> -\n> -               if (!*slash)\n> -                       break;\n> -\n> -               next_component = slash + 1;\n> -               while (is_dir_sep(*next_component))\n> -                       next_component++;\n> -               if (!*next_component)\n> -                       break;\n> -\n> -               slash_character = *slash;\n> -               *slash = '\\0';\n> -               if (!stat(path, &st)) {\n> -                       /* path exists */\n> -                       if (!S_ISDIR(st.st_mode)) {\n> -                               errno = ENOTDIR;\n> -                               ret = SCLD_EXISTS;\n> -                       }\n> -               } else if (mkdir(path, 0777)) {\n> -                       if (errno == EEXIST &&\n> -                           !stat(path, &st) && S_ISDIR(st.st_mode))\n> -                               ; /* somebody created it since we checked */\n> -                       else if (errno == ENOENT)\n> -                               /*\n> -                                * Either mkdir() failed because\n> -                                * somebody just pruned the containing\n> -                                * directory, or stat() failed because\n> -                                * the file that was in our way was\n> -                                * just removed.  Either way, inform\n> -                                * the caller that it might be worth\n> -                                * trying again:\n> -                                */\n> -                               ret = SCLD_VANISHED;\n> -                       else\n> -                               ret = SCLD_FAILED;\n> -               } else if (share && adjust_shared_perm(the_repository, path)) {\n> -                       ret = SCLD_PERMS;\n> -               }\n> -               *slash = slash_character;\n> -       }\n> -       return ret;\n> -}\n> -\n> -enum scld_error safe_create_leading_directories(char *path)\n> -{\n> -       return safe_create_leading_directories_1(path, 1);\n> -}\n> -\n> -enum scld_error safe_create_leading_directories_no_share(char *path)\n> -{\n> -       return safe_create_leading_directories_1(path, 0);\n> -}\n> -\n> -enum scld_error safe_create_leading_directories_const(const char *path)\n> -{\n> -       int save_errno;\n> -       /* path points to cache entries, so xstrdup before messing with it */\n> -       char *buf = xstrdup(path);\n> -       enum scld_error result = safe_create_leading_directories(buf);\n> -\n> -       save_errno = errno;\n> -       free(buf);\n> -       errno = save_errno;\n> -       return result;\n> -}\n> -\n>  int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n>  {\n>         int fd;\n> diff --git a/object-file.h b/object-file.h\n> index 81b30d269c8..922f2bba8c9 100644\n> --- a/object-file.h\n> +++ b/object-file.h\n> @@ -21,41 +21,6 @@ extern int fetch_if_missing;\n>  int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n>  int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n>\n> -/*\n> - * Create the directory containing the named path, using care to be\n> - * somewhat safe against races. Return one of the scld_error values to\n> - * indicate success/failure. On error, set errno to describe the\n> - * problem.\n> - *\n> - * SCLD_VANISHED indicates that one of the ancestor directories of the\n> - * path existed at one point during the function call and then\n> - * suddenly vanished, probably because another process pruned the\n> - * directory while we were working.  To be robust against this kind of\n> - * race, callers might want to try invoking the function again when it\n> - * returns SCLD_VANISHED.\n> - *\n> - * safe_create_leading_directories() temporarily changes path while it\n> - * is working but restores it before returning.\n> - * safe_create_leading_directories_const() doesn't modify path, even\n> - * temporarily. Both these variants adjust the permissions of the\n> - * created directories to honor core.sharedRepository, so they are best\n> - * suited for files inside the git dir. For working tree files, use\n> - * safe_create_leading_directories_no_share() instead, as it ignores\n> - * the core.sharedRepository setting.\n> - */\n> -enum scld_error {\n> -       SCLD_OK = 0,\n> -       SCLD_FAILED = -1,\n> -       SCLD_PERMS = -2,\n> -       SCLD_EXISTS = -3,\n> -       SCLD_VANISHED = -4\n> -};\n> -enum scld_error safe_create_leading_directories(char *path);\n> -enum scld_error safe_create_leading_directories_const(const char *path);\n> -enum scld_error safe_create_leading_directories_no_share(char *path);\n> -\n> -int mkdir_in_gitdir(const char *path);\n> -\n>  int git_open_cloexec(const char *name, int flags);\n>  #define git_open(name) git_open_cloexec(name, O_RDONLY)\n>\n>\n> --\n> 2.49.0.682.gc9b6a7b2b0.dirty\n>\n>\n"},{"id":"515916","messageId":"CABPp-BFL+4=vggNR8unMR9-TN04NmzqBqtd+sM1J27k70DMFGA@mail.gmail.com","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-3-f1fd50191143@pks.im","subject":"Re: [PATCH 3/9] object-file: move `xmmap()` into \"wrapper.c\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-04-09T14:36:54Z","receivedAt":"2025-04-09T14:37:07Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Apr 8, 2025 at 3:33 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> The `xmmap()` function is provided by \"object-file.c\" even though its\n> functionality got nothing to do with the object file subsystem. Move it\n\ns/got/has/ ?\n\n\n> into \"wrapper.c\", whose header already declares those functions.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  object-file.c | 48 ------------------------------------------------\n>  wrapper.c     | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n>  2 files changed, 48 insertions(+), 48 deletions(-)\n>\n> diff --git a/object-file.c b/object-file.c\n> index 1a20c7fa072..ea2ed7628e6 100644\n> --- a/object-file.c\n> +++ b/object-file.c\n> @@ -719,54 +719,6 @@ int has_loose_object(const struct object_id *oid)\n>         return check_and_freshen(oid, 0);\n>  }\n>\n> -static void mmap_limit_check(size_t length)\n> -{\n> -       static size_t limit = 0;\n> -       if (!limit) {\n> -               limit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n> -               if (!limit)\n> -                       limit = SIZE_MAX;\n> -       }\n> -       if (length > limit)\n> -               die(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n> -                   (uintmax_t)length, (uintmax_t)limit);\n> -}\n> -\n> -void *xmmap_gently(void *start, size_t length,\n> -                 int prot, int flags, int fd, off_t offset)\n> -{\n> -       void *ret;\n> -\n> -       mmap_limit_check(length);\n> -       ret = mmap(start, length, prot, flags, fd, offset);\n> -       if (ret == MAP_FAILED && !length)\n> -               ret = NULL;\n> -       return ret;\n> -}\n> -\n> -const char *mmap_os_err(void)\n> -{\n> -       static const char blank[] = \"\";\n> -#if defined(__linux__)\n> -       if (errno == ENOMEM) {\n> -               /* this continues an existing error message: */\n> -               static const char enomem[] =\n> -\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n> -               return enomem;\n> -       }\n> -#endif /* OS-specific bits */\n> -       return blank;\n> -}\n> -\n> -void *xmmap(void *start, size_t length,\n> -       int prot, int flags, int fd, off_t offset)\n> -{\n> -       void *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n> -       if (ret == MAP_FAILED)\n> -               die_errno(_(\"mmap failed%s\"), mmap_os_err());\n> -       return ret;\n> -}\n> -\n>  static int format_object_header_literally(char *str, size_t size,\n>                                           const char *type, size_t objsize)\n>  {\n> diff --git a/wrapper.c b/wrapper.c\n> index 8b985931490..3c79778055e 100644\n> --- a/wrapper.c\n> +++ b/wrapper.c\n> @@ -829,3 +829,51 @@ uint32_t git_rand(unsigned flags)\n>\n>         return result;\n>  }\n> +\n> +static void mmap_limit_check(size_t length)\n> +{\n> +       static size_t limit = 0;\n> +       if (!limit) {\n> +               limit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n> +               if (!limit)\n> +                       limit = SIZE_MAX;\n> +       }\n> +       if (length > limit)\n> +               die(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n> +                   (uintmax_t)length, (uintmax_t)limit);\n> +}\n> +\n> +void *xmmap_gently(void *start, size_t length,\n> +                 int prot, int flags, int fd, off_t offset)\n> +{\n> +       void *ret;\n> +\n> +       mmap_limit_check(length);\n> +       ret = mmap(start, length, prot, flags, fd, offset);\n> +       if (ret == MAP_FAILED && !length)\n> +               ret = NULL;\n> +       return ret;\n> +}\n> +\n> +const char *mmap_os_err(void)\n> +{\n> +       static const char blank[] = \"\";\n> +#if defined(__linux__)\n> +       if (errno == ENOMEM) {\n> +               /* this continues an existing error message: */\n> +               static const char enomem[] =\n> +\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n> +               return enomem;\n> +       }\n> +#endif /* OS-specific bits */\n> +       return blank;\n> +}\n> +\n> +void *xmmap(void *start, size_t length,\n> +       int prot, int flags, int fd, off_t offset)\n> +{\n> +       void *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n> +       if (ret == MAP_FAILED)\n> +               die_errno(_(\"mmap failed%s\"), mmap_os_err());\n> +       return ret;\n> +}\n>\n> --\n> 2.49.0.682.gc9b6a7b2b0.dirty\n>\n>\n"},{"id":"515917","messageId":"CABPp-BFv_WGVUexyQBHkCcyO0N+UQCcJ3eM9Oh6bP_ZCUkaxLw@mail.gmail.com","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"Re: [PATCH 0/9] Split up \"object-file.c\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-04-09T14:42:38Z","receivedAt":"2025-04-09T14:42:52Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Apr 8, 2025 at 3:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> Hi,\n>\n> \"object-file.c\" is quite a grab-bag of all kinds of different functions.\n> Many of these functions aren't really a good fit though and should be\n> owned by a different subsystem. This patch series tries to split up\n> concerns a bit better by splitting out this functionality into other\n> files:\n>\n>   - `safe_create_leading_directories()` is moved into \"dir.c\".\n>   - `xmmap()` is moved into \"wrapper.c\".\n>   - `git_open_cloexec()` is moved into \"compat/open.c\".\n>   - Several functions attached to `struct index_state` are moved into\n>     \"read-cache.c\".\n>   - Several functions related to `struct object_store` are moved into a\n>     new file \"object-store.c\".\n>\n> \"object-file.c\" now mostly contains logic to read and write loose object\n> files, whereas \"object-store.c\" contains the higher-level logic to\n> manage different object directories for a repository. Eventually, these\n> will become the loose object backend as well as the `struct ref_store`\n> equivalent for objects, respectively.\n>\n> The series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\n> with ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\n> branch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\n> into it.\n\nI was able to apply with these instructions, and looking over the\nseries locally with --color-moved was very helpful to verify that you\nwere moving functions without modifying them (or, to notice where you\ndid modify them and why, e.g. removing the static declaration).\n\nI very much appreciate the grouping of functions by area that is done\nhere with read-cache.c, wrapper.c, compat/open.c, dir.c, and the new\nobject-store.c, as well as the new enums to make the purpose of the\nHASH_* flags clearer, and the reduction in the number of globals.\n\nI only had a few comments on a couple of the patch commit messages.\n"},{"id":"515976","messageId":"Z_jgUw1SLS9IlTHk@pks.im","threadId":"63270","inReplyTo":"xmqqsemiteot.fsf@gitster.g","subject":"Re: [PATCH 0/9] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:26:43Z","receivedAt":"2025-04-11T09:26:53Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Apr 08, 2025 at 04:29:38PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > The series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\n> > with ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\n> > branch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\n> > into it.\n> \n> As 9442b1c919a is not public, this description lacks a way to verify\n> the result I attempted to reproduce.  ps/object-wo-the-repository I\n> have ends with 7d70b29c (hash: stop depending on `the_repository` in\n> `null_oid()`, 2025-03-10) and it hasn't moved for a while, so\n> hopefully we are in agreement ;-)\n\nUgh, yeah, I meant to refer to the tip of that branch indeed, which does\npoint to the commit you mention.\n\n> There were a few unpleasant interactions with other topics in\n> flight; please check the conflict resolution I made.\n\nI couldn't spot anything wrong. Thanks!\n\nPatrick\n"},{"id":"515977","messageId":"Z_jgdV7Tkw0hkvgj@pks.im","threadId":"63270","inReplyTo":"CABPp-BFpU5iLUN6Fh_+UG2Y593TWp4E+C_QQxLg6b=Cb-30F6A@mail.gmail.com","subject":"Re: [PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:27:17Z","receivedAt":"2025-04-11T09:27:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Apr 09, 2025 at 07:36:47AM -0700, Elijah Newren wrote:\n> On Tue, Apr 8, 2025 at 3:37 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > The `safe_create_leading_directories()` function and its relatives\n> \n> How is mkdir_in_gitdir() a relative of safe_create_leading_directories()?\n> \n> I assumed the relation was \"called by\", but there is no such\n> relationship.  The rest of the patch looked fine, but I was puzzled\n> for a while trying to figure out what this relationship is.\n\nIt's more of a sibling than a child/parent in this case, true. I still\nthink it makes sense to move it around as it is rather generic in the\nfunctionality it provides and doesn't have anything to do with objects.\n\nPatrick\n"},{"id":"515978","messageId":"Z_jgeWbEAMjYUSBQ@pks.im","threadId":"63270","inReplyTo":"CABPp-BFv_WGVUexyQBHkCcyO0N+UQCcJ3eM9Oh6bP_ZCUkaxLw@mail.gmail.com","subject":"Re: [PATCH 0/9] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:27:21Z","receivedAt":"2025-04-11T09:27:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Apr 09, 2025 at 07:42:38AM -0700, Elijah Newren wrote:\n> On Tue, Apr 8, 2025 at 3:35 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > Hi,\n> >\n> > \"object-file.c\" is quite a grab-bag of all kinds of different functions.\n> > Many of these functions aren't really a good fit though and should be\n> > owned by a different subsystem. This patch series tries to split up\n> > concerns a bit better by splitting out this functionality into other\n> > files:\n> >\n> >   - `safe_create_leading_directories()` is moved into \"dir.c\".\n> >   - `xmmap()` is moved into \"wrapper.c\".\n> >   - `git_open_cloexec()` is moved into \"compat/open.c\".\n> >   - Several functions attached to `struct index_state` are moved into\n> >     \"read-cache.c\".\n> >   - Several functions related to `struct object_store` are moved into a\n> >     new file \"object-store.c\".\n> >\n> > \"object-file.c\" now mostly contains logic to read and write loose object\n> > files, whereas \"object-store.c\" contains the higher-level logic to\n> > manage different object directories for a repository. Eventually, these\n> > will become the loose object backend as well as the `struct ref_store`\n> > equivalent for objects, respectively.\n> >\n> > The series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\n> > with ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\n> > branch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\n> > into it.\n> \n> I was able to apply with these instructions, and looking over the\n> series locally with --color-moved was very helpful to verify that you\n> were moving functions without modifying them (or, to notice where you\n> did modify them and why, e.g. removing the static declaration).\n> \n> I very much appreciate the grouping of functions by area that is done\n> here with read-cache.c, wrapper.c, compat/open.c, dir.c, and the new\n> object-store.c, as well as the new enums to make the purpose of the\n> HASH_* flags clearer, and the reduction in the number of globals.\n> \n> I only had a few comments on a couple of the patch commit messages.\n\nThanks for your review!\n\nPatrick\n"},{"id":"515980","messageId":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH v2 0/9] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:49Z","receivedAt":"2025-04-11T09:29:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\n\"object-file.c\" is quite a grab-bag of all kinds of different functions.\nMany of these functions aren't really a good fit though and should be\nowned by a different subsystem. This patch series tries to split up\nconcerns a bit better by splitting out this functionality into other\nfiles:\n\n  - `safe_create_leading_directories()` is moved into \"dir.c\".\n  - `xmmap()` is moved into \"wrapper.c\".\n  - `git_open_cloexec()` is moved into \"compat/open.c\".\n  - Several functions attached to `struct index_state` are moved into\n    \"read-cache.c\".\n  - Several functions related to `struct object_store` are moved into a\n    new file \"object-store.c\".\n\n\"object-file.c\" now mostly contains logic to read and write loose object\nfiles, whereas \"object-store.c\" contains the higher-level logic to\nmanage different object directories for a repository. Eventually, these\nwill become the loose object backend as well as the `struct ref_store`\nequivalent for objects, respectively.\n\nThe series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\nwith ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\nbranch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\ninto it.\n\nChanges in v2:\n  - Fix a grammar issue in one of the commit messages.\n  - Link to v1: https://lore.kernel.org/r/20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (9):\n      object-file: move `safe_create_leading_directories()` into \"dir.c\"\n      object-file: move `git_open_cloexec()` to \"compat/open.c\"\n      object-file: move `xmmap()` into \"wrapper.c\"\n      object-file: split out functions relating to object store subsystem\n      object-file: split up concerns of `HASH_*` flags\n      object-file: split out functions relating to index subsystem\n      object: split out functions relating to object store subsystem\n      object-store: remove global array of cached objects\n      object-store: merge \"object-store-ll.h\" and \"object-store.h\"\n\n Makefile                           |    3 +-\n apply.c                            |    2 +-\n archive-tar.c                      |    2 +-\n archive-zip.c                      |    2 +-\n archive.c                          |    2 +-\n attr.c                             |    2 +-\n bisect.c                           |    2 +-\n blame.c                            |    4 +-\n builtin/backfill.c                 |    2 +-\n builtin/blame.c                    |    2 +-\n builtin/bugreport.c                |    2 +-\n builtin/cat-file.c                 |    2 +-\n builtin/checkout.c                 |    3 +-\n builtin/clone.c                    |    2 +-\n builtin/commit-graph.c             |    2 +-\n builtin/commit-tree.c              |    2 +-\n builtin/count-objects.c            |    2 +-\n builtin/credential-cache--daemon.c |    2 +-\n builtin/describe.c                 |    2 +-\n builtin/diagnose.c                 |    2 +-\n builtin/difftool.c                 |    4 +-\n builtin/fast-export.c              |    2 +-\n builtin/fast-import.c              |    2 +-\n builtin/fetch.c                    |    2 +-\n builtin/fsck.c                     |    3 +-\n builtin/gc.c                       |    4 +-\n builtin/grep.c                     |    2 +-\n builtin/hash-object.c              |   26 +-\n builtin/index-pack.c               |    2 +-\n builtin/init-db.c                  |    2 +-\n builtin/log.c                      |    4 +-\n builtin/ls-tree.c                  |    2 +-\n builtin/merge-file.c               |    1 +\n builtin/merge-tree.c               |    2 +-\n builtin/mktag.c                    |    2 +-\n builtin/mktree.c                   |    3 +-\n builtin/multi-pack-index.c         |    2 +-\n builtin/notes.c                    |    3 +-\n builtin/pack-objects.c             |    2 +-\n builtin/pack-redundant.c           |    2 +-\n builtin/prune.c                    |    2 +-\n builtin/receive-pack.c             |    3 +-\n builtin/remote.c                   |    2 +-\n builtin/repack.c                   |    2 +-\n builtin/replace.c                  |    5 +-\n builtin/rev-list.c                 |    2 +-\n builtin/show-ref.c                 |    2 +-\n builtin/submodule--helper.c        |    2 +-\n builtin/tag.c                      |    3 +-\n builtin/unpack-file.c              |    3 +-\n builtin/unpack-objects.c           |    3 +-\n builtin/update-index.c             |    2 +-\n bulk-checkin.c                     |    9 +-\n bundle-uri.c                       |    2 +-\n bundle.c                           |    2 +-\n cache-tree.c                       |    4 +-\n combine-diff.c                     |    2 +-\n commit-graph.c                     |    4 +-\n commit-graph.h                     |    2 +-\n commit.c                           |    3 +-\n compat/open.c                      |   29 +\n config.c                           |    2 +-\n connected.c                        |    2 +-\n convert.c                          |    2 +-\n diagnose.c                         |    2 +-\n diff.c                             |    4 +-\n diffcore-rename.c                  |    2 +-\n dir.c                              |  109 ++-\n dir.h                              |   35 +\n entry.c                            |    2 +-\n fetch-pack.c                       |    2 +-\n fmt-merge-msg.c                    |    2 +-\n fsck.c                             |    2 +-\n git-compat-util.h                  |    3 +\n grep.c                             |    2 +-\n http-backend.c                     |    2 +-\n http-push.c                        |    3 +-\n http-walker.c                      |    2 +-\n http.c                             |    2 +-\n list-objects-filter.c              |    2 +-\n list-objects.c                     |    2 +-\n log-tree.c                         |    2 +-\n mailmap.c                          |    2 +-\n match-trees.c                      |    3 +-\n merge-blobs.c                      |    2 +-\n merge-ort.c                        |    3 +-\n merge-recursive.c                  |    2 +-\n meson.build                        |    2 +\n midx-write.c                       |    1 +\n midx.c                             |    1 -\n notes-cache.c                      |    3 +-\n notes-merge.c                      |    5 +-\n notes.c                            |    3 +-\n object-file.c                      | 1420 +-----------------------------------\n object-file.h                      |  118 +--\n object-name.c                      |    2 +-\n object-store-ll.h                  |  556 --------------\n object-store.c                     | 1050 ++++++++++++++++++++++++++\n object-store.h                     |  516 ++++++++++++-\n object.c                           |   67 --\n oss-fuzz/fuzz-pack-idx.c           |    2 +-\n pack-bitmap-write.c                |    2 +-\n pack-bitmap.c                      |    3 +-\n pack-check.c                       |    2 +-\n pack-mtimes.c                      |    3 +-\n pack-objects.h                     |    2 +-\n pack-revindex.c                    |    3 +-\n packfile.c                         |    2 +-\n path.c                             |    2 +-\n promisor-remote.c                  |    2 +-\n protocol-caps.c                    |    2 +-\n prune-packed.c                     |    2 +-\n reachable.c                        |    2 +-\n read-cache.c                       |  230 +++++-\n read-cache.h                       |    9 +\n ref-filter.c                       |    2 +-\n reflog.c                           |    2 +-\n refs.c                             |    2 +-\n remote.c                           |    2 +-\n replace-object.c                   |    2 +-\n replace-object.h                   |    2 +-\n repository.c                       |    2 +-\n rerere.c                           |    2 +-\n revision.c                         |    2 +-\n send-pack.c                        |    2 +-\n sequencer.c                        |    2 +-\n server-info.c                      |    2 +-\n shallow.c                          |    2 +-\n streaming.c                        |    2 +-\n submodule-config.c                 |    2 +-\n submodule.c                        |    2 +-\n t/helper/test-pack-mtimes.c        |    2 +-\n t/helper/test-partial-clone.c      |    2 +-\n t/helper/test-read-graph.c         |    2 +-\n t/helper/test-read-midx.c          |    2 +-\n t/helper/test-ref-store.c          |    2 +-\n tag.c                              |    2 +-\n tmp-objdir.c                       |    2 +-\n tree-walk.c                        |    2 +-\n tree.c                             |    2 +-\n unpack-trees.c                     |    2 +-\n upload-pack.c                      |    2 +-\n walker.c                           |    2 +-\n wrapper.c                          |   48 ++\n xdiff-interface.c                  |    2 +-\n 145 files changed, 2296 insertions(+), 2224 deletions(-)\n\nRange-diff versus v1:\n\n 1:  f1b7bd0550e =  1:  ad5f2af9380 object-file: move `safe_create_leading_directories()` into \"dir.c\"\n 2:  859e33f9b6e =  2:  2ae1de46a37 object-file: move `git_open_cloexec()` to \"compat/open.c\"\n 3:  f676828ab77 !  3:  18145eb4a7d object-file: move `xmmap()` into \"wrapper.c\"\n    @@ Commit message\n         object-file: move `xmmap()` into \"wrapper.c\"\n     \n         The `xmmap()` function is provided by \"object-file.c\" even though its\n    -    functionality got nothing to do with the object file subsystem. Move it\n    +    functionality has nothing to do with the object file subsystem. Move it\n         into \"wrapper.c\", whose header already declares those functions.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n 4:  1ed615206a5 =  4:  944883c3ad0 object-file: split out functions relating to object store subsystem\n 5:  2d6354ebb65 =  5:  fed8a395b4f object-file: split up concerns of `HASH_*` flags\n 6:  1d97b65731d =  6:  292c4d911ff object-file: split out functions relating to index subsystem\n 7:  47fb869121a =  7:  478209b9109 object: split out functions relating to object store subsystem\n 8:  bd8ce7dcc57 =  8:  e446ade793b object-store: remove global array of cached objects\n 9:  69e337e58f0 =  9:  d2f0e4e52a1 object-store: merge \"object-store-ll.h\" and \"object-store.h\"\n\n---\nbase-commit: 9442b1c919af9aed513eb0a484fe96358a500cf5\nchange-id: 20250408-pks-split-object-file-c61d7cd2a21f\n\n"},{"id":"515981","messageId":"20250411-pks-split-object-file-v2-1-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:50Z","receivedAt":"2025-04-11T09:29:58Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `safe_create_leading_directories()` function and its relatives are\nlocated in \"object-file.c\", which is not a good fit as they provide\ngeneric functionality not related to objects at all. Move them into\n\"dir.c\".\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/bugreport.c                |   2 +-\n builtin/credential-cache--daemon.c |   2 +-\n builtin/diagnose.c                 |   2 +-\n builtin/fsck.c                     |   1 +\n builtin/gc.c                       |   2 +-\n builtin/init-db.c                  |   2 +-\n builtin/log.c                      |   2 +-\n commit-graph.c                     |   1 +\n dir.c                              | 107 ++++++++++++++++++++++++++++++++++++-\n dir.h                              |  35 ++++++++++++\n midx-write.c                       |   1 +\n object-file.c                      | 106 ------------------------------------\n object-file.h                      |  35 ------------\n 13 files changed, 150 insertions(+), 148 deletions(-)\n\ndiff --git a/builtin/bugreport.c b/builtin/bugreport.c\nindex 66d64bfd5ae..d07fa91c247 100644\n--- a/builtin/bugreport.c\n+++ b/builtin/bugreport.c\n@@ -1,6 +1,7 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"editor.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n@@ -10,7 +11,6 @@\n #include \"hook.h\"\n #include \"hook-list.h\"\n #include \"diagnose.h\"\n-#include \"object-file.h\"\n #include \"setup.h\"\n #include \"version.h\"\n \ndiff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c\nindex e707618e743..80d29b4f5c0 100644\n--- a/builtin/credential-cache--daemon.c\n+++ b/builtin/credential-cache--daemon.c\n@@ -1,8 +1,8 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n \n #ifndef NO_UNIX_SOCKETS\ndiff --git a/builtin/diagnose.c b/builtin/diagnose.c\nindex 33c39bd5981..d5dadd6a48b 100644\n--- a/builtin/diagnose.c\n+++ b/builtin/diagnose.c\n@@ -2,8 +2,8 @@\n \n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n #include \"diagnose.h\"\n \ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 9c8a6d6a8df..32d40d8f9fc 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -1,5 +1,6 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"config.h\"\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 99431fd4674..b069629676c 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -16,6 +16,7 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"date.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"config.h\"\n@@ -28,7 +29,6 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 196dccdd77a..39730c1b0ce 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -6,9 +6,9 @@\n #define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"abspath.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"refs.h\"\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 0d4c579dad7..06ffaa93e86 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -10,11 +10,11 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n-#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"pager.h\"\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 8286d5dda24..3fae20dc21b 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -4,6 +4,7 @@\n #include \"git-compat-util.h\"\n #include \"config.h\"\n #include \"csum-file.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\ndiff --git a/dir.c b/dir.c\nindex 28b0e03feb4..16ae3b5169d 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,6 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\n #include \"refs.h\"\n@@ -4132,3 +4131,109 @@ int path_match_flags(const char *const str, const enum path_match_flags flags)\n \t\treturn is_xplatform_dir_sep(*p);\n \tBUG(\"unreachable\");\n }\n+\n+int mkdir_in_gitdir(const char *path)\n+{\n+\tif (mkdir(path, 0777)) {\n+\t\tint saved_errno = errno;\n+\t\tstruct stat st;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (errno != EEXIST)\n+\t\t\treturn -1;\n+\t\t/*\n+\t\t * Are we looking at a path in a symlinked worktree\n+\t\t * whose original repository does not yet have it?\n+\t\t * e.g. .git/rr-cache pointing at its original\n+\t\t * repository in which the user hasn't performed any\n+\t\t * conflict resolution yet?\n+\t\t */\n+\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n+\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n+\t\t    !is_absolute_path(sb.buf) ||\n+\t\t    mkdir(sb.buf, 0777)) {\n+\t\t\tstrbuf_release(&sb);\n+\t\t\terrno = saved_errno;\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrbuf_release(&sb);\n+\t}\n+\treturn adjust_shared_perm(the_repository, path);\n+}\n+\n+static enum scld_error safe_create_leading_directories_1(char *path, int share)\n+{\n+\tchar *next_component = path + offset_1st_component(path);\n+\tenum scld_error ret = SCLD_OK;\n+\n+\twhile (ret == SCLD_OK && next_component) {\n+\t\tstruct stat st;\n+\t\tchar *slash = next_component, slash_character;\n+\n+\t\twhile (*slash && !is_dir_sep(*slash))\n+\t\t\tslash++;\n+\n+\t\tif (!*slash)\n+\t\t\tbreak;\n+\n+\t\tnext_component = slash + 1;\n+\t\twhile (is_dir_sep(*next_component))\n+\t\t\tnext_component++;\n+\t\tif (!*next_component)\n+\t\t\tbreak;\n+\n+\t\tslash_character = *slash;\n+\t\t*slash = '\\0';\n+\t\tif (!stat(path, &st)) {\n+\t\t\t/* path exists */\n+\t\t\tif (!S_ISDIR(st.st_mode)) {\n+\t\t\t\terrno = ENOTDIR;\n+\t\t\t\tret = SCLD_EXISTS;\n+\t\t\t}\n+\t\t} else if (mkdir(path, 0777)) {\n+\t\t\tif (errno == EEXIST &&\n+\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t; /* somebody created it since we checked */\n+\t\t\telse if (errno == ENOENT)\n+\t\t\t\t/*\n+\t\t\t\t * Either mkdir() failed because\n+\t\t\t\t * somebody just pruned the containing\n+\t\t\t\t * directory, or stat() failed because\n+\t\t\t\t * the file that was in our way was\n+\t\t\t\t * just removed.  Either way, inform\n+\t\t\t\t * the caller that it might be worth\n+\t\t\t\t * trying again:\n+\t\t\t\t */\n+\t\t\t\tret = SCLD_VANISHED;\n+\t\t\telse\n+\t\t\t\tret = SCLD_FAILED;\n+\t\t} else if (share && adjust_shared_perm(the_repository, path)) {\n+\t\t\tret = SCLD_PERMS;\n+\t\t}\n+\t\t*slash = slash_character;\n+\t}\n+\treturn ret;\n+}\n+\n+enum scld_error safe_create_leading_directories(char *path)\n+{\n+\treturn safe_create_leading_directories_1(path, 1);\n+}\n+\n+enum scld_error safe_create_leading_directories_no_share(char *path)\n+{\n+\treturn safe_create_leading_directories_1(path, 0);\n+}\n+\n+enum scld_error safe_create_leading_directories_const(const char *path)\n+{\n+\tint save_errno;\n+\t/* path points to cache entries, so xstrdup before messing with it */\n+\tchar *buf = xstrdup(path);\n+\tenum scld_error result = safe_create_leading_directories(buf);\n+\n+\tsave_errno = errno;\n+\tfree(buf);\n+\terrno = save_errno;\n+\treturn result;\n+}\ndiff --git a/dir.h b/dir.h\nindex d7e71aa8daa..02c1f9420b0 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -676,4 +676,39 @@ static inline int starts_with_dot_dot_slash_native(const char *const path)\n \treturn path_match_flags(path, what | PATH_MATCH_NATIVE);\n }\n \n+/*\n+ * Create the directory containing the named path, using care to be\n+ * somewhat safe against races. Return one of the scld_error values to\n+ * indicate success/failure. On error, set errno to describe the\n+ * problem.\n+ *\n+ * SCLD_VANISHED indicates that one of the ancestor directories of the\n+ * path existed at one point during the function call and then\n+ * suddenly vanished, probably because another process pruned the\n+ * directory while we were working.  To be robust against this kind of\n+ * race, callers might want to try invoking the function again when it\n+ * returns SCLD_VANISHED.\n+ *\n+ * safe_create_leading_directories() temporarily changes path while it\n+ * is working but restores it before returning.\n+ * safe_create_leading_directories_const() doesn't modify path, even\n+ * temporarily. Both these variants adjust the permissions of the\n+ * created directories to honor core.sharedRepository, so they are best\n+ * suited for files inside the git dir. For working tree files, use\n+ * safe_create_leading_directories_no_share() instead, as it ignores\n+ * the core.sharedRepository setting.\n+ */\n+enum scld_error {\n+\tSCLD_OK = 0,\n+\tSCLD_FAILED = -1,\n+\tSCLD_PERMS = -2,\n+\tSCLD_EXISTS = -3,\n+\tSCLD_VANISHED = -4\n+};\n+enum scld_error safe_create_leading_directories(char *path);\n+enum scld_error safe_create_leading_directories_const(const char *path);\n+enum scld_error safe_create_leading_directories_no_share(char *path);\n+\n+int mkdir_in_gitdir(const char *path);\n+\n #endif\ndiff --git a/midx-write.c b/midx-write.c\nindex a628ac24dcb..e01a867c583 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -3,6 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n #include \"packfile.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 772c311f188..23b2c8560be 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -91,112 +91,6 @@ static int get_conv_flags(unsigned flags)\n }\n \n \n-int mkdir_in_gitdir(const char *path)\n-{\n-\tif (mkdir(path, 0777)) {\n-\t\tint saved_errno = errno;\n-\t\tstruct stat st;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (errno != EEXIST)\n-\t\t\treturn -1;\n-\t\t/*\n-\t\t * Are we looking at a path in a symlinked worktree\n-\t\t * whose original repository does not yet have it?\n-\t\t * e.g. .git/rr-cache pointing at its original\n-\t\t * repository in which the user hasn't performed any\n-\t\t * conflict resolution yet?\n-\t\t */\n-\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n-\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n-\t\t    !is_absolute_path(sb.buf) ||\n-\t\t    mkdir(sb.buf, 0777)) {\n-\t\t\tstrbuf_release(&sb);\n-\t\t\terrno = saved_errno;\n-\t\t\treturn -1;\n-\t\t}\n-\t\tstrbuf_release(&sb);\n-\t}\n-\treturn adjust_shared_perm(the_repository, path);\n-}\n-\n-static enum scld_error safe_create_leading_directories_1(char *path, int share)\n-{\n-\tchar *next_component = path + offset_1st_component(path);\n-\tenum scld_error ret = SCLD_OK;\n-\n-\twhile (ret == SCLD_OK && next_component) {\n-\t\tstruct stat st;\n-\t\tchar *slash = next_component, slash_character;\n-\n-\t\twhile (*slash && !is_dir_sep(*slash))\n-\t\t\tslash++;\n-\n-\t\tif (!*slash)\n-\t\t\tbreak;\n-\n-\t\tnext_component = slash + 1;\n-\t\twhile (is_dir_sep(*next_component))\n-\t\t\tnext_component++;\n-\t\tif (!*next_component)\n-\t\t\tbreak;\n-\n-\t\tslash_character = *slash;\n-\t\t*slash = '\\0';\n-\t\tif (!stat(path, &st)) {\n-\t\t\t/* path exists */\n-\t\t\tif (!S_ISDIR(st.st_mode)) {\n-\t\t\t\terrno = ENOTDIR;\n-\t\t\t\tret = SCLD_EXISTS;\n-\t\t\t}\n-\t\t} else if (mkdir(path, 0777)) {\n-\t\t\tif (errno == EEXIST &&\n-\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n-\t\t\t\t; /* somebody created it since we checked */\n-\t\t\telse if (errno == ENOENT)\n-\t\t\t\t/*\n-\t\t\t\t * Either mkdir() failed because\n-\t\t\t\t * somebody just pruned the containing\n-\t\t\t\t * directory, or stat() failed because\n-\t\t\t\t * the file that was in our way was\n-\t\t\t\t * just removed.  Either way, inform\n-\t\t\t\t * the caller that it might be worth\n-\t\t\t\t * trying again:\n-\t\t\t\t */\n-\t\t\t\tret = SCLD_VANISHED;\n-\t\t\telse\n-\t\t\t\tret = SCLD_FAILED;\n-\t\t} else if (share && adjust_shared_perm(the_repository, path)) {\n-\t\t\tret = SCLD_PERMS;\n-\t\t}\n-\t\t*slash = slash_character;\n-\t}\n-\treturn ret;\n-}\n-\n-enum scld_error safe_create_leading_directories(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 1);\n-}\n-\n-enum scld_error safe_create_leading_directories_no_share(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 0);\n-}\n-\n-enum scld_error safe_create_leading_directories_const(const char *path)\n-{\n-\tint save_errno;\n-\t/* path points to cache entries, so xstrdup before messing with it */\n-\tchar *buf = xstrdup(path);\n-\tenum scld_error result = safe_create_leading_directories(buf);\n-\n-\tsave_errno = errno;\n-\tfree(buf);\n-\terrno = save_errno;\n-\treturn result;\n-}\n-\n int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n {\n \tint fd;\ndiff --git a/object-file.h b/object-file.h\nindex 81b30d269c8..922f2bba8c9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,41 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-/*\n- * Create the directory containing the named path, using care to be\n- * somewhat safe against races. Return one of the scld_error values to\n- * indicate success/failure. On error, set errno to describe the\n- * problem.\n- *\n- * SCLD_VANISHED indicates that one of the ancestor directories of the\n- * path existed at one point during the function call and then\n- * suddenly vanished, probably because another process pruned the\n- * directory while we were working.  To be robust against this kind of\n- * race, callers might want to try invoking the function again when it\n- * returns SCLD_VANISHED.\n- *\n- * safe_create_leading_directories() temporarily changes path while it\n- * is working but restores it before returning.\n- * safe_create_leading_directories_const() doesn't modify path, even\n- * temporarily. Both these variants adjust the permissions of the\n- * created directories to honor core.sharedRepository, so they are best\n- * suited for files inside the git dir. For working tree files, use\n- * safe_create_leading_directories_no_share() instead, as it ignores\n- * the core.sharedRepository setting.\n- */\n-enum scld_error {\n-\tSCLD_OK = 0,\n-\tSCLD_FAILED = -1,\n-\tSCLD_PERMS = -2,\n-\tSCLD_EXISTS = -3,\n-\tSCLD_VANISHED = -4\n-};\n-enum scld_error safe_create_leading_directories(char *path);\n-enum scld_error safe_create_leading_directories_const(const char *path);\n-enum scld_error safe_create_leading_directories_no_share(char *path);\n-\n-int mkdir_in_gitdir(const char *path);\n-\n int git_open_cloexec(const char *name, int flags);\n #define git_open(name) git_open_cloexec(name, O_RDONLY)\n \n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515982","messageId":"20250411-pks-split-object-file-v2-2-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 2/9] object-file: move `git_open_cloexec()` to \"compat/open.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:51Z","receivedAt":"2025-04-11T09:30:00Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `git_open_cloexec()` wrapper function provides the ability to open a\nfile with `O_CLOEXEC` in a platform-agnostic way. This function is\nprovided by \"object-file.c\" even though it is not specific to the object\nsubsystem at all.\n\nMove the file into \"compat/open.c\". This file already exists before this\ncommit, but has only been compiled conditionally depending on whether or\nnot open(3p) may return EINTR. With this change we now unconditionally\ncompile the object, but wrap `git_open_with_retry()` in an ifdef.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile          |  2 +-\n commit-graph.c    |  1 -\n compat/open.c     | 29 +++++++++++++++++++++++++++++\n git-compat-util.h |  3 +++\n meson.build       |  1 +\n midx.c            |  1 -\n object-file.c     | 27 ---------------------------\n object-file.h     |  3 ---\n pack-bitmap.c     |  1 -\n pack-mtimes.c     |  1 -\n pack-revindex.c   |  1 -\n 11 files changed, 34 insertions(+), 36 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex c41fc41ef0e..bb5407b4703 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -994,6 +994,7 @@ LIB_OBJS += common-exit.o\n LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n+LIB_OBJS += compat/open.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += compiler-tricks/not-constant.o\n LIB_OBJS += config.o\n@@ -1812,7 +1813,6 @@ ifdef FREAD_READS_DIRECTORIES\n endif\n ifdef OPEN_RETURNS_EINTR\n \tCOMPAT_CFLAGS += -DOPEN_RETURNS_EINTR\n-\tCOMPAT_OBJS += compat/open.o\n endif\n ifdef NO_SYMLINK_HEAD\n \tBASIC_CFLAGS += -DNO_SYMLINK_HEAD\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 3fae20dc21b..8060c358b84 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -14,7 +14,6 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\ndiff --git a/compat/open.c b/compat/open.c\nindex eb3754a23b8..37ae2b1aeb9 100644\n--- a/compat/open.c\n+++ b/compat/open.c\n@@ -1,5 +1,6 @@\n #include \"git-compat-util.h\"\n \n+#ifdef OPEN_RETURNS_EINTR\n #undef open\n int git_open_with_retry(const char *path, int flags, ...)\n {\n@@ -23,3 +24,31 @@ int git_open_with_retry(const char *path, int flags, ...)\n \n \treturn ret;\n }\n+#endif\n+\n+int git_open_cloexec(const char *name, int flags)\n+{\n+\tint fd;\n+\tstatic int o_cloexec = O_CLOEXEC;\n+\n+\tfd = open(name, flags | o_cloexec);\n+\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n+\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n+\t\to_cloexec &= ~O_CLOEXEC;\n+\t\tfd = open(name, flags | o_cloexec);\n+\t}\n+\n+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n+\t{\n+\t\tstatic int fd_cloexec = FD_CLOEXEC;\n+\n+\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n+\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n+\t\t\tint flags = fcntl(fd, F_GETFD);\n+\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n+\t\t\t\tfd_cloexec = 0;\n+\t\t}\n+\t}\n+#endif\n+\treturn fd;\n+}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex cf733b38acd..9273a8ee087 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1000,6 +1000,9 @@ int git_vsnprintf(char *str, size_t maxsize,\n int git_open_with_retry(const char *path, int flag, ...);\n #endif\n \n+int git_open_cloexec(const char *name, int flags);\n+#define git_open(name) git_open_cloexec(name, O_RDONLY)\n+\n #ifdef __GLIBC_PREREQ\n #if __GLIBC_PREREQ(2, 1)\n #define HAVE_STRCHRNUL\ndiff --git a/meson.build b/meson.build\nindex 145d2f7ff9e..a55e800b85b 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -263,6 +263,7 @@ libgit_sources = [\n   'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n+  'compat/open.c',\n   'compat/terminal.c',\n   'compiler-tricks/not-constant.c',\n   'config.c',\ndiff --git a/midx.c b/midx.c\nindex 807fdf72f7b..3d0015f7828 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -5,7 +5,6 @@\n #include \"dir.h\"\n #include \"hex.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"hash-lookup.h\"\n #include \"midx.h\"\n #include \"progress.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 23b2c8560be..1a20c7fa072 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -834,33 +834,6 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \treturn !oideq(oid, &real_oid) ? -1 : 0;\n }\n \n-int git_open_cloexec(const char *name, int flags)\n-{\n-\tint fd;\n-\tstatic int o_cloexec = O_CLOEXEC;\n-\n-\tfd = open(name, flags | o_cloexec);\n-\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n-\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n-\t\to_cloexec &= ~O_CLOEXEC;\n-\t\tfd = open(name, flags | o_cloexec);\n-\t}\n-\n-#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n-\t{\n-\t\tstatic int fd_cloexec = FD_CLOEXEC;\n-\n-\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n-\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n-\t\t\tint flags = fcntl(fd, F_GETFD);\n-\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n-\t\t\t\tfd_cloexec = 0;\n-\t\t}\n-\t}\n-#endif\n-\treturn fd;\n-}\n-\n /*\n  * Find \"oid\" as a loose object in the local repository or in an alternate.\n  * Returns 0 on success, negative on failure.\ndiff --git a/object-file.h b/object-file.h\nindex 922f2bba8c9..353d8a85c33 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,9 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-int git_open_cloexec(const char *name, int flags);\n-#define git_open(name) git_open_cloexec(name, O_RDONLY)\n-\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 7fd78c634ef..0dbd7c4ffe1 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,6 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex cdf30b8d2b0..bcea28e521d 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 038e0c96b1c..1ee7b49e206 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515983","messageId":"20250411-pks-split-object-file-v2-3-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 3/9] object-file: move `xmmap()` into \"wrapper.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:52Z","receivedAt":"2025-04-11T09:30:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `xmmap()` function is provided by \"object-file.c\" even though its\nfunctionality has nothing to do with the object file subsystem. Move it\ninto \"wrapper.c\", whose header already declares those functions.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-file.c | 48 ------------------------------------------------\n wrapper.c     | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 48 insertions(+), 48 deletions(-)\n\ndiff --git a/object-file.c b/object-file.c\nindex 1a20c7fa072..ea2ed7628e6 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -719,54 +719,6 @@ int has_loose_object(const struct object_id *oid)\n \treturn check_and_freshen(oid, 0);\n }\n \n-static void mmap_limit_check(size_t length)\n-{\n-\tstatic size_t limit = 0;\n-\tif (!limit) {\n-\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n-\t\tif (!limit)\n-\t\t\tlimit = SIZE_MAX;\n-\t}\n-\tif (length > limit)\n-\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n-\t\t    (uintmax_t)length, (uintmax_t)limit);\n-}\n-\n-void *xmmap_gently(void *start, size_t length,\n-\t\t  int prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret;\n-\n-\tmmap_limit_check(length);\n-\tret = mmap(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED && !length)\n-\t\tret = NULL;\n-\treturn ret;\n-}\n-\n-const char *mmap_os_err(void)\n-{\n-\tstatic const char blank[] = \"\";\n-#if defined(__linux__)\n-\tif (errno == ENOMEM) {\n-\t\t/* this continues an existing error message: */\n-\t\tstatic const char enomem[] =\n-\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n-\t\treturn enomem;\n-\t}\n-#endif /* OS-specific bits */\n-\treturn blank;\n-}\n-\n-void *xmmap(void *start, size_t length,\n-\tint prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED)\n-\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n-\treturn ret;\n-}\n-\n static int format_object_header_literally(char *str, size_t size,\n \t\t\t\t\t  const char *type, size_t objsize)\n {\ndiff --git a/wrapper.c b/wrapper.c\nindex 8b985931490..3c79778055e 100644\n--- a/wrapper.c\n+++ b/wrapper.c\n@@ -829,3 +829,51 @@ uint32_t git_rand(unsigned flags)\n \n \treturn result;\n }\n+\n+static void mmap_limit_check(size_t length)\n+{\n+\tstatic size_t limit = 0;\n+\tif (!limit) {\n+\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n+\t\tif (!limit)\n+\t\t\tlimit = SIZE_MAX;\n+\t}\n+\tif (length > limit)\n+\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n+\t\t    (uintmax_t)length, (uintmax_t)limit);\n+}\n+\n+void *xmmap_gently(void *start, size_t length,\n+\t\t  int prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret;\n+\n+\tmmap_limit_check(length);\n+\tret = mmap(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED && !length)\n+\t\tret = NULL;\n+\treturn ret;\n+}\n+\n+const char *mmap_os_err(void)\n+{\n+\tstatic const char blank[] = \"\";\n+#if defined(__linux__)\n+\tif (errno == ENOMEM) {\n+\t\t/* this continues an existing error message: */\n+\t\tstatic const char enomem[] =\n+\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n+\t\treturn enomem;\n+\t}\n+#endif /* OS-specific bits */\n+\treturn blank;\n+}\n+\n+void *xmmap(void *start, size_t length,\n+\tint prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED)\n+\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n+\treturn ret;\n+}\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515985","messageId":"20250411-pks-split-object-file-v2-4-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 4/9] object-file: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:53Z","receivedAt":"2025-04-11T09:30:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"While we have the \"object-store.h\" header, most of the functionality for\nobject stores is actually hosted in \"object-file.c\". This makes it hard\nto find relevant functions and causes us to mix up concerns.\n\nSplit out functions relating to the object store subsystem into a new\n\"object-store.c\" file.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile                 |   1 +\n builtin/checkout.c       |   1 +\n builtin/merge-file.c     |   1 +\n builtin/mktree.c         |   1 +\n builtin/notes.c          |   1 +\n builtin/receive-pack.c   |   1 +\n builtin/tag.c            |   1 +\n builtin/unpack-file.c    |   1 +\n builtin/unpack-objects.c |   1 +\n commit.c                 |   1 +\n http-push.c              |   1 +\n match-trees.c            |   3 +-\n merge-ort.c              |   3 +-\n meson.build              |   1 +\n notes-cache.c            |   3 +-\n notes.c                  |   3 +-\n object-file.c            | 991 +----------------------------------------------\n object-file.h            |  68 +++-\n object-store-ll.h        |  60 +--\n object-store.c           | 972 ++++++++++++++++++++++++++++++++++++++++++++++\n 20 files changed, 1074 insertions(+), 1041 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex bb5407b4703..d8ad76fb791 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1086,6 +1086,7 @@ LIB_OBJS += notes.o\n LIB_OBJS += object-file-convert.o\n LIB_OBJS += object-file.o\n LIB_OBJS += object-name.o\n+LIB_OBJS += object-store.o\n LIB_OBJS += object.o\n LIB_OBJS += oid-array.o\n LIB_OBJS += oidmap.o\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e7486cf658..3e68623838a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -18,6 +18,7 @@\n #include \"lockfile.h\"\n #include \"mem-pool.h\"\n #include \"merge-recursive.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 7e315f374b2..2b16b10d2ca 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -5,6 +5,7 @@\n #include \"abspath.h\"\n #include \"diff.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store.h\"\n #include \"config.h\"\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 3c16faa40e3..0644f951161 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -11,6 +11,7 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"parse-options.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n \n static struct treeent {\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex ff61ec5f2da..0dbc233752d 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -14,6 +14,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex b3e2a9d0c60..ee51bd76f60 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -31,6 +31,7 @@\n #include \"tmp-objdir.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7c173535cb3..536a01ff3ae 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex fb5fcbc40a8..b19e5cabd03 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -2,6 +2,7 @@\n #include \"builtin.h\"\n #include \"config.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 3bbcaf2de9b..4078eab9252 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -8,6 +8,7 @@\n #include \"gettext.h\"\n #include \"git-zlib.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"object.h\"\n #include \"delta.h\"\ndiff --git a/commit.c b/commit.c\nindex 48aeefaad31..fbf4f8e87fd 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -29,6 +29,7 @@\n #include \"tree.h\"\n #include \"hook.h\"\n #include \"parse.h\"\n+#include \"object-file.h\"\n #include \"object-file-convert.h\"\n \n static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);\ndiff --git a/http-push.c b/http-push.c\nindex 1b030d96f48..806eb67cf1b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -19,6 +19,7 @@\n #include \"tree-walk.h\"\n #include \"url.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"commit-reach.h\"\n \ndiff --git a/match-trees.c b/match-trees.c\nindex ef14ceb594c..72922d5d64e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -6,7 +6,8 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n static int score_missing(unsigned mode)\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 1554900d984..5de3e2fc159 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -36,8 +36,9 @@\n #include \"merge-ll.h\"\n #include \"match-trees.h\"\n #include \"mem-pool.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"promisor-remote.h\"\ndiff --git a/meson.build b/meson.build\nindex a55e800b85b..f656f978020 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -355,6 +355,7 @@ libgit_sources = [\n   'object-file-convert.c',\n   'object-file.c',\n   'object-name.c',\n+  'object-store.c',\n   'object.c',\n   'oid-array.c',\n   'oidmap.c',\ndiff --git a/notes-cache.c b/notes-cache.c\nindex ecfdf6e43b5..150241b15e0 100644\n--- a/notes-cache.c\n+++ b/notes-cache.c\n@@ -2,7 +2,8 @@\n \n #include \"git-compat-util.h\"\n #include \"notes-cache.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"repository.h\"\n #include \"commit.h\"\ndiff --git a/notes.c b/notes.c\nindex ce5a1006a83..d9645c4b5dc 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -6,8 +6,9 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\ndiff --git a/object-file.c b/object-file.c\nindex ea2ed7628e6..1c74aa239fb 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -11,75 +11,26 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"abspath.h\"\n-#include \"config.h\"\n+#include \"bulk-checkin.h\"\n #include \"convert.h\"\n #include \"environment.h\"\n+#include \"fsck.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"string-list.h\"\n-#include \"lockfile.h\"\n-#include \"pack.h\"\n-#include \"commit.h\"\n-#include \"run-command.h\"\n-#include \"refs.h\"\n-#include \"bulk-checkin.h\"\n-#include \"repository.h\"\n-#include \"replace-object.h\"\n-#include \"streaming.h\"\n-#include \"dir.h\"\n-#include \"list.h\"\n-#include \"quote.h\"\n-#include \"packfile.h\"\n+#include \"loose.h\"\n+#include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n #include \"oidtree.h\"\n+#include \"pack.h\"\n+#include \"packfile.h\"\n #include \"path.h\"\n-#include \"promisor-remote.h\"\n #include \"setup.h\"\n-#include \"submodule.h\"\n-#include \"fsck.h\"\n-#include \"loose.h\"\n-#include \"object-file-convert.h\"\n+#include \"streaming.h\"\n \n /* The maximum size for an object header. */\n #define MAX_HEADER_LEN 32\n \n-/*\n- * This is meant to hold a *small* number of objects that you would\n- * want repo_read_object_file() to be able to return, but yet you do not want\n- * to write them into the object store (e.g. a browse-only\n- * application).\n- */\n-static struct cached_object_entry {\n-\tstruct object_id oid;\n-\tstruct cached_object {\n-\t\tenum object_type type;\n-\t\tconst void *buf;\n-\t\tunsigned long size;\n-\t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n-\n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n-{\n-\tstatic const struct cached_object empty_tree = {\n-\t\t.type = OBJ_TREE,\n-\t\t.buf = \"\",\n-\t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n-\n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n-\t\tif (oideq(&co->oid, oid))\n-\t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n-\t\treturn &empty_tree;\n-\treturn NULL;\n-}\n-\n-\n static int get_conv_flags(unsigned flags)\n {\n \tif (flags & HASH_RENORMALIZE)\n@@ -90,40 +41,6 @@ static int get_conv_flags(unsigned flags)\n \t\treturn 0;\n }\n \n-\n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n-{\n-\tint fd;\n-\t/*\n-\t * we let the umask do its job, don't try to be more\n-\t * restrictive except to remove write permission.\n-\t */\n-\tint mode = 0444;\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\t/* some mkstemp implementations erase temp_filename on failure */\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tsafe_create_leading_directories(temp_filename->buf);\n-\treturn xmkstemp_mode(temp_filename->buf, mode);\n-}\n-\n-int odb_pack_keep(const char *name)\n-{\n-\tint fd;\n-\n-\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\tsafe_create_leading_directories_const(name);\n-\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-}\n-\n static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n {\n \tint i;\n@@ -137,9 +54,9 @@ static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n \t}\n }\n \n-static const char *odb_loose_path(struct object_directory *odb,\n-\t\t\t\t  struct strbuf *buf,\n-\t\t\t\t  const struct object_id *oid)\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid)\n {\n \tstrbuf_reset(buf);\n \tstrbuf_addstr(buf, odb->path);\n@@ -148,513 +65,6 @@ static const char *odb_loose_path(struct object_directory *odb,\n \treturn buf->buf;\n }\n \n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid)\n-{\n-\treturn odb_loose_path(r->objects->odb, buf, oid);\n-}\n-\n-/*\n- * Return non-zero iff the path is usable as an alternate object database.\n- */\n-static int alt_odb_usable(struct raw_object_store *o,\n-\t\t\t  struct strbuf *path,\n-\t\t\t  const char *normalized_objdir, khiter_t *pos)\n-{\n-\tint r;\n-\n-\t/* Detect cases where alternate disappeared */\n-\tif (!is_directory(path->buf)) {\n-\t\terror(_(\"object directory %s does not exist; \"\n-\t\t\t\"check .git/objects/info/alternates\"),\n-\t\t      path->buf);\n-\t\treturn 0;\n-\t}\n-\n-\t/*\n-\t * Prevent the common mistake of listing the same\n-\t * thing twice, or object directory itself.\n-\t */\n-\tif (!o->odb_by_path) {\n-\t\tkhiter_t p;\n-\n-\t\to->odb_by_path = kh_init_odb_path_map();\n-\t\tassert(!o->odb->next);\n-\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n-\t\tassert(r == 1); /* never used */\n-\t\tkh_value(o->odb_by_path, p) = o->odb;\n-\t}\n-\tif (fspatheq(path->buf, normalized_objdir))\n-\t\treturn 0;\n-\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n-\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n-\treturn r == 0 ? 0 : 1;\n-}\n-\n-/*\n- * Prepare alternate object database registry.\n- *\n- * The variable alt_odb_list points at the list of struct\n- * object_directory.  The elements on this list come from\n- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n- * whose contents is similar to that environment variable but can be\n- * LF separated.  Its base points at a statically allocated buffer that\n- * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n- * its name points just after the slash at the end of \".git/objects/\"\n- * in the example above, and has enough space to hold all hex characters\n- * of the object ID, an extra slash for the first level indirection, and\n- * the terminating NUL.\n- */\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth);\n-static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n-\tconst char *relative_base, int depth, const char *normalized_objdir)\n-{\n-\tstruct object_directory *ent;\n-\tstruct strbuf pathbuf = STRBUF_INIT;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\tkhiter_t pos;\n-\tint ret = -1;\n-\n-\tif (!is_absolute_path(entry->buf) && relative_base) {\n-\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n-\t\tstrbuf_addch(&pathbuf, '/');\n-\t}\n-\tstrbuf_addbuf(&pathbuf, entry);\n-\n-\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n-\t\terror(_(\"unable to normalize alternate object path: %s\"),\n-\t\t      pathbuf.buf);\n-\t\tgoto error;\n-\t}\n-\tstrbuf_swap(&pathbuf, &tmp);\n-\n-\t/*\n-\t * The trailing slash after the directory name is given by\n-\t * this function at the end. Remove duplicates.\n-\t */\n-\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n-\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n-\n-\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n-\t\tgoto error;\n-\n-\tCALLOC_ARRAY(ent, 1);\n-\t/* pathbuf.buf is already in r->objects->odb_by_path */\n-\tent->path = strbuf_detach(&pathbuf, NULL);\n-\n-\t/* add the alternate entry */\n-\t*r->objects->odb_tail = ent;\n-\tr->objects->odb_tail = &(ent->next);\n-\tent->next = NULL;\n-\tassert(r->objects->odb_by_path);\n-\tkh_value(r->objects->odb_by_path, pos) = ent;\n-\n-\t/* recursively add alternates */\n-\tread_info_alternates(r, ent->path, depth + 1);\n-\tret = 0;\n- error:\n-\tstrbuf_release(&tmp);\n-\tstrbuf_release(&pathbuf);\n-\treturn ret;\n-}\n-\n-static const char *parse_alt_odb_entry(const char *string,\n-\t\t\t\t       int sep,\n-\t\t\t\t       struct strbuf *out)\n-{\n-\tconst char *end;\n-\n-\tstrbuf_reset(out);\n-\n-\tif (*string == '#') {\n-\t\t/* comment; consume up to next separator */\n-\t\tend = strchrnul(string, sep);\n-\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n-\t\t/*\n-\t\t * quoted path; unquote_c_style has copied the\n-\t\t * data for us and set \"end\". Broken quoting (e.g.,\n-\t\t * an entry that doesn't end with a quote) falls\n-\t\t * back to the unquoted case below.\n-\t\t */\n-\t} else {\n-\t\t/* normal, unquoted path */\n-\t\tend = strchrnul(string, sep);\n-\t\tstrbuf_add(out, string, end - string);\n-\t}\n-\n-\tif (*end)\n-\t\tend++;\n-\treturn end;\n-}\n-\n-static void link_alt_odb_entries(struct repository *r, const char *alt,\n-\t\t\t\t int sep, const char *relative_base, int depth)\n-{\n-\tstruct strbuf objdirbuf = STRBUF_INIT;\n-\tstruct strbuf entry = STRBUF_INIT;\n-\n-\tif (!alt || !*alt)\n-\t\treturn;\n-\n-\tif (depth > 5) {\n-\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n-\t\t\t\trelative_base);\n-\t\treturn;\n-\t}\n-\n-\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n-\n-\twhile (*alt) {\n-\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n-\t\tif (!entry.len)\n-\t\t\tcontinue;\n-\t\tlink_alt_odb_entry(r, &entry,\n-\t\t\t\t   relative_base, depth, objdirbuf.buf);\n-\t}\n-\tstrbuf_release(&entry);\n-\tstrbuf_release(&objdirbuf);\n-}\n-\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth)\n-{\n-\tchar *path;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n-\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n-\t\twarn_on_fopen_errors(path);\n-\t\tfree(path);\n-\t\treturn;\n-\t}\n-\n-\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n-\tstrbuf_release(&buf);\n-\tfree(path);\n-}\n-\n-void add_to_alternates_file(const char *reference)\n-{\n-\tstruct lock_file lock = LOCK_INIT;\n-\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n-\tFILE *in, *out;\n-\tint found = 0;\n-\n-\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n-\tout = fdopen_lock_file(&lock, \"w\");\n-\tif (!out)\n-\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n-\n-\tin = fopen(alts, \"r\");\n-\tif (in) {\n-\t\tstruct strbuf line = STRBUF_INIT;\n-\n-\t\twhile (strbuf_getline(&line, in) != EOF) {\n-\t\t\tif (!strcmp(reference, line.buf)) {\n-\t\t\t\tfound = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n-\t\t}\n-\n-\t\tstrbuf_release(&line);\n-\t\tfclose(in);\n-\t}\n-\telse if (errno != ENOENT)\n-\t\tdie_errno(_(\"unable to read alternates file\"));\n-\n-\tif (found) {\n-\t\trollback_lock_file(&lock);\n-\t} else {\n-\t\tfprintf_or_die(out, \"%s\\n\", reference);\n-\t\tif (commit_lock_file(&lock))\n-\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n-\t\tif (the_repository->objects->loaded_alternates)\n-\t\t\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t\t\t     '\\n', NULL, 0);\n-\t}\n-\tfree(alts);\n-}\n-\n-void add_to_alternates_memory(const char *reference)\n-{\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t     '\\n', NULL, 0);\n-}\n-\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n-{\n-\tstruct object_directory *new_odb;\n-\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\t/*\n-\t * Make a new primary odb and link the old primary ODB in as an\n-\t * alternate\n-\t */\n-\tnew_odb = xcalloc(1, sizeof(*new_odb));\n-\tnew_odb->path = xstrdup(dir);\n-\n-\t/*\n-\t * Disable ref updates while a temporary odb is active, since\n-\t * the objects in the database may roll back.\n-\t */\n-\tnew_odb->disable_ref_updates = 1;\n-\tnew_odb->will_destroy = will_destroy;\n-\tnew_odb->next = the_repository->objects->odb;\n-\tthe_repository->objects->odb = new_odb;\n-\treturn new_odb->next;\n-}\n-\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n-{\n-\tstruct object_directory *cur_odb = the_repository->objects->odb;\n-\n-\tif (strcmp(old_path, cur_odb->path))\n-\t\tBUG(\"expected %s as primary object store; found %s\",\n-\t\t    old_path, cur_odb->path);\n-\n-\tif (cur_odb->next != restore_odb)\n-\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n-\n-\tthe_repository->objects->odb = restore_odb;\n-\tfree_object_directory(cur_odb);\n-}\n-\n-/*\n- * Compute the exact path an alternate is at and returns it. In case of\n- * error NULL is returned and the human readable error is added to `err`\n- * `path` may be relative and should point to $GIT_DIR.\n- * `err` must not be null.\n- */\n-char *compute_alternate_path(const char *path, struct strbuf *err)\n-{\n-\tchar *ref_git = NULL;\n-\tconst char *repo;\n-\tint seen_error = 0;\n-\n-\tref_git = real_pathdup(path, 0);\n-\tif (!ref_git) {\n-\t\tseen_error = 1;\n-\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\trepo = read_gitfile(ref_git);\n-\tif (!repo)\n-\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n-\tif (repo) {\n-\t\tfree(ref_git);\n-\t\tref_git = xstrdup(repo);\n-\t}\n-\n-\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n-\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n-\t\tfree(ref_git);\n-\t\tref_git = ref_git_git;\n-\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tseen_error = 1;\n-\t\tif (get_common_dir(&sb, ref_git)) {\n-\t\t\tstrbuf_addf(err,\n-\t\t\t\t    _(\"reference repository '%s' as a linked \"\n-\t\t\t\t      \"checkout is not supported yet.\"),\n-\t\t\t\t    path);\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n-\t\t\t\t\t\"local repository.\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err,\n-\t\t\t    _(\"reference repository '%s' is grafted\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-out:\n-\tif (seen_error) {\n-\t\tFREE_AND_NULL(ref_git);\n-\t}\n-\n-\treturn ref_git;\n-}\n-\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n-{\n-\tstruct object_directory *odb;\n-\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n-\tstruct strbuf odb_path_real = STRBUF_INIT;\n-\n-\tprepare_alt_odb(r);\n-\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n-\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n-\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n-\t\t\tbreak;\n-\t}\n-\n-\tfree(obj_dir_real);\n-\tstrbuf_release(&odb_path_real);\n-\n-\tif (!odb)\n-\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n-\treturn odb;\n-}\n-\n-static void fill_alternate_refs_command(struct child_process *cmd,\n-\t\t\t\t\tconst char *repo_path)\n-{\n-\tconst char *value;\n-\n-\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n-\t\tcmd->use_shell = 1;\n-\n-\t\tstrvec_push(&cmd->args, value);\n-\t\tstrvec_push(&cmd->args, repo_path);\n-\t} else {\n-\t\tcmd->git_cmd = 1;\n-\n-\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n-\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n-\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n-\n-\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n-\t\t\tstrvec_push(&cmd->args, \"--\");\n-\t\t\tstrvec_split(&cmd->args, value);\n-\t\t}\n-\t}\n-\n-\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n-\tcmd->out = -1;\n-}\n-\n-static void read_alternate_refs(const char *path,\n-\t\t\t\talternate_ref_fn *cb,\n-\t\t\t\tvoid *data)\n-{\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tstruct strbuf line = STRBUF_INIT;\n-\tFILE *fh;\n-\n-\tfill_alternate_refs_command(&cmd, path);\n-\n-\tif (start_command(&cmd))\n-\t\treturn;\n-\n-\tfh = xfdopen(cmd.out, \"r\");\n-\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n-\t\tstruct object_id oid;\n-\t\tconst char *p;\n-\n-\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n-\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n-\t\t\t\tline.buf);\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tcb(&oid, data);\n-\t}\n-\n-\tfclose(fh);\n-\tfinish_command(&cmd);\n-\tstrbuf_release(&line);\n-}\n-\n-struct alternate_refs_data {\n-\talternate_ref_fn *fn;\n-\tvoid *data;\n-};\n-\n-static int refs_from_alternate_cb(struct object_directory *e,\n-\t\t\t\t  void *data)\n-{\n-\tstruct strbuf path = STRBUF_INIT;\n-\tsize_t base_len;\n-\tstruct alternate_refs_data *cb = data;\n-\n-\tif (!strbuf_realpath(&path, e->path, 0))\n-\t\tgoto out;\n-\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n-\t\tgoto out;\n-\tbase_len = path.len;\n-\n-\t/* Is this a git repository with refs? */\n-\tstrbuf_addstr(&path, \"/refs\");\n-\tif (!is_directory(path.buf))\n-\t\tgoto out;\n-\tstrbuf_setlen(&path, base_len);\n-\n-\tread_alternate_refs(path.buf, cb->fn, cb->data);\n-\n-out:\n-\tstrbuf_release(&path);\n-\treturn 0;\n-}\n-\n-void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n-{\n-\tstruct alternate_refs_data cb;\n-\tcb.fn = fn;\n-\tcb.data = data;\n-\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n-}\n-\n-int foreach_alt_odb(alt_odb_fn fn, void *cb)\n-{\n-\tstruct object_directory *ent;\n-\tint r = 0;\n-\n-\tprepare_alt_odb(the_repository);\n-\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n-\t\tr = fn(ent, cb);\n-\t\tif (r)\n-\t\t\tbreak;\n-\t}\n-\treturn r;\n-}\n-\n-void prepare_alt_odb(struct repository *r)\n-{\n-\tif (r->objects->loaded_alternates)\n-\t\treturn;\n-\n-\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n-\n-\tread_info_alternates(r, r->objects->odb->path, 0);\n-\tr->objects->loaded_alternates = 1;\n-}\n-\n-int has_alt_odb(struct repository *r)\n-{\n-\tprepare_alt_odb(r);\n-\treturn !!r->objects->odb->next;\n-}\n-\n /* Returns 1 if we have successfully freshened the file, 0 otherwise. */\n static int freshen_file(const char *fn)\n {\n@@ -1056,9 +466,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)\n \treturn 0;\n }\n \n-static int loose_object_info(struct repository *r,\n-\t\t\t     const struct object_id *oid,\n-\t\t\t     struct object_info *oi, int flags)\n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags)\n {\n \tint status = 0;\n \tint fd;\n@@ -1154,345 +564,6 @@ static int loose_object_info(struct repository *r,\n \treturn status;\n }\n \n-int obj_read_use_lock = 0;\n-pthread_mutex_t obj_read_mutex;\n-\n-void enable_obj_read_lock(void)\n-{\n-\tif (obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 1;\n-\tinit_recursive_mutex(&obj_read_mutex);\n-}\n-\n-void disable_obj_read_lock(void)\n-{\n-\tif (!obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 0;\n-\tpthread_mutex_destroy(&obj_read_mutex);\n-}\n-\n-int fetch_if_missing = 1;\n-\n-static int do_oid_object_info_extended(struct repository *r,\n-\t\t\t\t       const struct object_id *oid,\n-\t\t\t\t       struct object_info *oi, unsigned flags)\n-{\n-\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n-\tconst struct cached_object *co;\n-\tstruct pack_entry e;\n-\tint rtype;\n-\tconst struct object_id *real = oid;\n-\tint already_retried = 0;\n-\n-\n-\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n-\t\treal = lookup_replace_object(r, oid);\n-\n-\tif (is_null_oid(real))\n-\t\treturn -1;\n-\n-\tif (!oi)\n-\t\toi = &blank_oi;\n-\n-\tco = find_cached_object(real);\n-\tif (co) {\n-\t\tif (oi->typep)\n-\t\t\t*(oi->typep) = co->type;\n-\t\tif (oi->sizep)\n-\t\t\t*(oi->sizep) = co->size;\n-\t\tif (oi->disk_sizep)\n-\t\t\t*(oi->disk_sizep) = 0;\n-\t\tif (oi->delta_base_oid)\n-\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n-\t\tif (oi->type_name)\n-\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n-\t\tif (oi->contentp)\n-\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n-\t\toi->whence = OI_CACHED;\n-\t\treturn 0;\n-\t}\n-\n-\twhile (1) {\n-\t\tif (find_pack_entry(r, real, &e))\n-\t\t\tbreak;\n-\n-\t\t/* Most likely it's a loose object. */\n-\t\tif (!loose_object_info(r, real, oi, flags))\n-\t\t\treturn 0;\n-\n-\t\t/* Not a loose object; someone else may have just packed it. */\n-\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n-\t\t\treprepare_packed_git(r);\n-\t\t\tif (find_pack_entry(r, real, &e))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\t/*\n-\t\t * If r is the_repository, this might be an attempt at\n-\t\t * accessing a submodule object as if it were in the_repository\n-\t\t * (having called add_submodule_odb() on that submodule's ODB).\n-\t\t * If any such ODBs exist, register them and try again.\n-\t\t */\n-\t\tif (r == the_repository &&\n-\t\t    register_all_submodule_odb_as_alternates())\n-\t\t\t/* We added some alternates; retry */\n-\t\t\tcontinue;\n-\n-\t\t/* Check if it is a missing object */\n-\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n-\t\t    !already_retried &&\n-\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n-\t\t\tpromisor_remote_get_direct(r, real, 1);\n-\t\t\talready_retried = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n-\t\t\tconst struct packed_git *p;\n-\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n-\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n-\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n-\t\t\tif ((p = has_packed_and_bad(r, real)))\n-\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n-\t\t\t\t    oid_to_hex(real), p->pack_name);\n-\t\t}\n-\t\treturn -1;\n-\t}\n-\n-\tif (oi == &blank_oi)\n-\t\t/*\n-\t\t * We know that the caller doesn't actually need the\n-\t\t * information below, so return early.\n-\t\t */\n-\t\treturn 0;\n-\trtype = packed_object_info(r, e.p, e.offset, oi);\n-\tif (rtype < 0) {\n-\t\tmark_bad_packed_object(e.p, real);\n-\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n-\t} else if (oi->whence == OI_PACKED) {\n-\t\toi->u.packed.offset = e.offset;\n-\t\toi->u.packed.pack = e.p;\n-\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n-\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int oid_object_info_convert(struct repository *r,\n-\t\t\t\t   const struct object_id *input_oid,\n-\t\t\t\t   struct object_info *input_oi, unsigned flags)\n-{\n-\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n-\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n-\tstruct strbuf type_name = STRBUF_INIT;\n-\tstruct object_id oid, delta_base_oid;\n-\tstruct object_info new_oi, *oi;\n-\tunsigned long size;\n-\tvoid *content;\n-\tint ret;\n-\n-\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n-\t\tif (do_die)\n-\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n-\t\treturn -1;\n-\t}\n-\n-\t/* Is new_oi needed? */\n-\toi = input_oi;\n-\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n-\t\t\t input_oi->contentp)) {\n-\t\tnew_oi = *input_oi;\n-\t\t/* Does delta_base_oid need to be converted? */\n-\t\tif (input_oi->delta_base_oid)\n-\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n-\t\t/* Will the attributes differ when converted? */\n-\t\tif (input_oi->sizep || input_oi->contentp) {\n-\t\t\tnew_oi.contentp = &content;\n-\t\t\tnew_oi.sizep = &size;\n-\t\t\tnew_oi.type_name = &type_name;\n-\t\t}\n-\t\toi = &new_oi;\n-\t}\n-\n-\tret = oid_object_info_extended(r, &oid, oi, flags);\n-\tif (ret)\n-\t\treturn -1;\n-\tif (oi == input_oi)\n-\t\treturn ret;\n-\n-\tif (new_oi.contentp) {\n-\t\tstruct strbuf outbuf = STRBUF_INIT;\n-\t\tenum object_type type;\n-\n-\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n-\t\t\t\t\t       !do_die);\n-\t\tif (type == -1)\n-\t\t\treturn -1;\n-\t\tif (type != OBJ_BLOB) {\n-\t\t\tret = convert_object_file(the_repository, &outbuf,\n-\t\t\t\t\t\t  the_hash_algo, input_algo,\n-\t\t\t\t\t\t  content, size, type, !do_die);\n-\t\t\tfree(content);\n-\t\t\tif (ret == -1)\n-\t\t\t\treturn -1;\n-\t\t\tsize = outbuf.len;\n-\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n-\t\t}\n-\t\tif (input_oi->sizep)\n-\t\t\t*input_oi->sizep = size;\n-\t\tif (input_oi->contentp)\n-\t\t\t*input_oi->contentp = content;\n-\t\telse\n-\t\t\tfree(content);\n-\t\tif (input_oi->type_name)\n-\t\t\t*input_oi->type_name = type_name;\n-\t\telse\n-\t\t\tstrbuf_release(&type_name);\n-\t}\n-\tif (new_oi.delta_base_oid == &delta_base_oid) {\n-\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n-\t\t\t\t input_oi->delta_base_oid)) {\n-\t\t\tif (do_die)\n-\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t\t    oid_to_hex(&delta_base_oid),\n-\t\t\t\t    input_algo->name);\n-\t\t\treturn -1;\n-\t\t}\n-\t}\n-\tinput_oi->whence = new_oi.whence;\n-\tinput_oi->u = new_oi.u;\n-\treturn ret;\n-}\n-\n-int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n-\t\t\t     struct object_info *oi, unsigned flags)\n-{\n-\tint ret;\n-\n-\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n-\t\treturn oid_object_info_convert(r, oid, oi, flags);\n-\n-\tobj_read_lock();\n-\tret = do_oid_object_info_extended(r, oid, oi, flags);\n-\tobj_read_unlock();\n-\treturn ret;\n-}\n-\n-\n-/* returns enum object_type or negative */\n-int oid_object_info(struct repository *r,\n-\t\t    const struct object_id *oid,\n-\t\t    unsigned long *sizep)\n-{\n-\tenum object_type type;\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\n-\toi.typep = &type;\n-\toi.sizep = sizep;\n-\tif (oid_object_info_extended(r, oid, &oi,\n-\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n-\t\treturn -1;\n-\treturn type;\n-}\n-\n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid)\n-{\n-\tstruct cached_object_entry *co;\n-\tchar *co_buf;\n-\n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n-\t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n-\tco->value.size = len;\n-\tco->value.type = type;\n-\tco_buf = xmalloc(len);\n-\tmemcpy(co_buf, buf, len);\n-\tco->value.buf = co_buf;\n-\toidcpy(&co->oid, oid);\n-\treturn 0;\n-}\n-\n-/*\n- * This function dies on corrupt objects; the callers who want to\n- * deal with them should arrange to call oid_object_info_extended() and give\n- * error messages themselves.\n- */\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size)\n-{\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n-\tvoid *data;\n-\n-\toi.typep = type;\n-\toi.sizep = size;\n-\toi.contentp = &data;\n-\tif (oid_object_info_extended(r, oid, &oi, flags))\n-\t\treturn NULL;\n-\n-\treturn data;\n-}\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *actual_oid_return)\n-{\n-\tenum object_type type;\n-\tvoid *buffer;\n-\tunsigned long isize;\n-\tstruct object_id actual_oid;\n-\n-\toidcpy(&actual_oid, oid);\n-\twhile (1) {\n-\t\tint ref_length = -1;\n-\t\tconst char *ref_type = NULL;\n-\n-\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n-\t\tif (!buffer)\n-\t\t\treturn NULL;\n-\t\tif (type == required_type) {\n-\t\t\t*size = isize;\n-\t\t\tif (actual_oid_return)\n-\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n-\t\t\treturn buffer;\n-\t\t}\n-\t\t/* Handle references */\n-\t\telse if (type == OBJ_COMMIT)\n-\t\t\tref_type = \"tree \";\n-\t\telse if (type == OBJ_TAG)\n-\t\t\tref_type = \"object \";\n-\t\telse {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tref_length = strlen(ref_type);\n-\n-\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n-\t\t    memcmp(buffer, ref_type, ref_length) ||\n-\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tfree(buffer);\n-\t\t/* Now we have the ID of the referred-to object in\n-\t\t * actual_oid.  Check again. */\n-\t}\n-}\n-\n static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,\n \t\t\t     const void *buf, unsigned long len,\n \t\t\t     struct object_id *oid,\n@@ -2154,32 +1225,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)\n \treturn ret;\n }\n \n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags)\n-{\n-\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n-\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n-\t\t(quick ? OBJECT_INFO_QUICK : 0);\n-\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n-}\n-\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags)\n-{\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n-}\n-\n-int repo_has_object_file(struct repository *r,\n-\t\t\t const struct object_id *oid)\n-{\n-\treturn repo_has_object_file_with_flags(r, oid, 0);\n-}\n-\n /*\n  * We can't use the normal fsck_error_function() for index_mem(),\n  * because we don't yet have a valid oid for it to report. Instead,\n@@ -2407,16 +1452,6 @@ int read_pack_header(int fd, struct pack_header *header)\n \treturn 0;\n }\n \n-void assert_oid_type(const struct object_id *oid, enum object_type expect)\n-{\n-\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n-\tif (type < 0)\n-\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n-\tif (type != expect)\n-\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n-\t\t    type_name(expect));\n-}\n-\n int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n \t\t\t\tstruct strbuf *path,\n \t\t\t\teach_loose_object_fn obj_cb,\ndiff --git a/object-file.h b/object-file.h\nindex 353d8a85c33..78c84d970a9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,6 +21,29 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n+struct object_directory;\n+\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid);\n+\n+/*\n+ * Return true iff an alternate object database has a loose object\n+ * with the specified name.  This function does not respect replace\n+ * references.\n+ */\n+int has_loose_object_nonlocal(const struct object_id *);\n+\n+int has_loose_object(const struct object_id *);\n+\n+/**\n+ * format_object_header() is a thin wrapper around s xsnprintf() that\n+ * writes the initial \"<type> <obj-len>\" part of the loose object\n+ * header. It returns the size that snprintf() returns + 1.\n+ */\n+int format_object_header(char *str, size_t size, enum object_type type,\n+\t\t\t size_t objsize);\n+\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\n@@ -61,6 +84,29 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+int write_object_file_flags(const void *buf, unsigned long len,\n+\t\t\t    enum object_type type, struct object_id *oid,\n+\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n+static inline int write_object_file(const void *buf, unsigned long len,\n+\t\t\t\t    enum object_type type, struct object_id *oid)\n+{\n+\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n+}\n+\n+struct input_stream {\n+\tconst void *(*read)(struct input_stream *, unsigned long *len);\n+\tvoid *data;\n+\tint is_finished;\n+};\n+\n+int write_object_file_literally(const void *buf, unsigned long len,\n+\t\t\t\tconst char *type, struct object_id *oid,\n+\t\t\t\tunsigned flags);\n+int stream_loose_object(struct input_stream *in_stream, size_t len,\n+\t\t\tstruct object_id *oid);\n+\n+int force_object_loose(const struct object_id *oid, time_t mtime);\n+\n /**\n  * With in-core object data in \"buf\", rehash it to make sure the\n  * object name actually matches \"oid\" to detect object corruption.\n@@ -79,6 +125,10 @@ int check_object_signature(struct repository *r, const struct object_id *oid,\n  */\n int stream_object_signature(struct repository *r, const struct object_id *oid);\n \n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags);\n+\n enum finalize_object_file_flags {\n \tFOF_SKIP_COLLISION_CHECK = 1,\n };\n@@ -90,10 +140,18 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,\n /* Helper to check and \"touch\" a file */\n int check_and_freshen_file(const char *fn, int freshen);\n \n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n+/*\n+ * Open the loose object at path, check its hash, and return the contents,\n+ * use the \"oi\" argument to assert things about the object, or e.g. populate its\n+ * type, and size. If the object is a blob, then \"contents\" may return NULL,\n+ * to allow streaming of large blobs.\n+ *\n+ * Returns 0 on success, negative on error (details may be written to stderr).\n+ */\n+int read_loose_object(const char *path,\n+\t\t      const struct object_id *expected_oid,\n+\t\t      struct object_id *real_oid,\n+\t\t      void **contents,\n+\t\t      struct object_info *oi);\n \n #endif /* OBJECT_FILE_H */\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex cd3bd5bd99f..8ae80b8a5fa 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -49,12 +49,6 @@ struct object_directory {\n \tchar *path;\n };\n \n-struct input_stream {\n-\tconst void *(*read)(struct input_stream *, unsigned long *len);\n-\tvoid *data;\n-\tint is_finished;\n-};\n-\n void prepare_alt_odb(struct repository *r);\n int has_alt_odb(struct repository *r);\n char *compute_alternate_path(const char *path, struct strbuf *err);\n@@ -273,21 +267,6 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n \t\t      unsigned long len, enum object_type type,\n \t\t      struct object_id *oid);\n \n-int write_object_file_flags(const void *buf, unsigned long len,\n-\t\t\t    enum object_type type, struct object_id *oid,\n-\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n-static inline int write_object_file(const void *buf, unsigned long len,\n-\t\t\t\t    enum object_type type, struct object_id *oid)\n-{\n-\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n-}\n-\n-int write_object_file_literally(const void *buf, unsigned long len,\n-\t\t\t\tconst char *type, struct object_id *oid,\n-\t\t\t\tunsigned flags);\n-int stream_loose_object(struct input_stream *in_stream, size_t len,\n-\t\t\tstruct object_id *oid);\n-\n /*\n  * Add an object file to the in-memory object store, without writing it\n  * to disk.\n@@ -299,8 +278,6 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,\n int pretend_object_file(void *, unsigned long, enum object_type,\n \t\t\tstruct object_id *oid);\n \n-int force_object_loose(const struct object_id *oid, time_t mtime);\n-\n struct object_info {\n \t/* Request */\n \tenum object_type *typep;\n@@ -364,20 +341,6 @@ int oid_object_info_extended(struct repository *r,\n \t\t\t     const struct object_id *,\n \t\t\t     struct object_info *, unsigned flags);\n \n-/*\n- * Open the loose object at path, check its hash, and return the contents,\n- * use the \"oi\" argument to assert things about the object, or e.g. populate its\n- * type, and size. If the object is a blob, then \"contents\" may return NULL,\n- * to allow streaming of large blobs.\n- *\n- * Returns 0 on success, negative on error (details may be written to stderr).\n- */\n-int read_loose_object(const char *path,\n-\t\t      const struct object_id *expected_oid,\n-\t\t      struct object_id *real_oid,\n-\t\t      void **contents,\n-\t\t      struct object_info *oi);\n-\n /* Retry packed storage after checking packed and loose storage */\n #define HAS_OBJECT_RECHECK_PACKED 1\n \n@@ -405,23 +368,6 @@ int repo_has_object_file(struct repository *r, const struct object_id *oid);\n int repo_has_object_file_with_flags(struct repository *r,\n \t\t\t\t    const struct object_id *oid, int flags);\n \n-/*\n- * Return true iff an alternate object database has a loose object\n- * with the specified name.  This function does not respect replace\n- * references.\n- */\n-int has_loose_object_nonlocal(const struct object_id *);\n-\n-int has_loose_object(const struct object_id *);\n-\n-/**\n- * format_object_header() is a thin wrapper around s xsnprintf() that\n- * writes the initial \"<type> <obj-len>\" part of the loose object\n- * header. It returns the size that snprintf() returns + 1.\n- */\n-int format_object_header(char *str, size_t size, enum object_type type,\n-\t\t\t size_t objsize);\n-\n void assert_oid_type(const struct object_id *oid, enum object_type expect);\n \n /*\n@@ -553,4 +499,10 @@ int for_each_object_in_pack(struct packed_git *p,\n int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n \t\t\t   void *data, enum for_each_object_flags flags);\n \n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n+\n #endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nnew file mode 100644\nindex 00000000000..e5f1f00cdde\n--- /dev/null\n+++ b/object-store.c\n@@ -0,0 +1,972 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"abspath.h\"\n+#include \"config.h\"\n+#include \"environment.h\"\n+#include \"gettext.h\"\n+#include \"hex.h\"\n+#include \"lockfile.h\"\n+#include \"object-file-convert.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n+#include \"packfile.h\"\n+#include \"path.h\"\n+#include \"promisor-remote.h\"\n+#include \"quote.h\"\n+#include \"replace-object.h\"\n+#include \"run-command.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"strvec.h\"\n+#include \"submodule.h\"\n+#include \"write-or-die.h\"\n+\n+/*\n+ * This is meant to hold a *small* number of objects that you would\n+ * want repo_read_object_file() to be able to return, but yet you do not want\n+ * to write them into the object store (e.g. a browse-only\n+ * application).\n+ */\n+static struct cached_object_entry {\n+\tstruct object_id oid;\n+\tstruct cached_object {\n+\t\tenum object_type type;\n+\t\tconst void *buf;\n+\t\tunsigned long size;\n+\t} value;\n+} *cached_objects;\n+static int cached_object_nr, cached_object_alloc;\n+\n+static const struct cached_object *find_cached_object(const struct object_id *oid)\n+{\n+\tstatic const struct cached_object empty_tree = {\n+\t\t.type = OBJ_TREE,\n+\t\t.buf = \"\",\n+\t};\n+\tint i;\n+\tconst struct cached_object_entry *co = cached_objects;\n+\n+\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\t\tif (oideq(&co->oid, oid))\n+\t\t\treturn &co->value;\n+\t}\n+\tif (oideq(oid, the_hash_algo->empty_tree))\n+\t\treturn &empty_tree;\n+\treturn NULL;\n+}\n+\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n+{\n+\tint fd;\n+\t/*\n+\t * we let the umask do its job, don't try to be more\n+\t * restrictive except to remove write permission.\n+\t */\n+\tint mode = 0444;\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\t/* some mkstemp implementations erase temp_filename on failure */\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tsafe_create_leading_directories(temp_filename->buf);\n+\treturn xmkstemp_mode(temp_filename->buf, mode);\n+}\n+\n+int odb_pack_keep(const char *name)\n+{\n+\tint fd;\n+\n+\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\tsafe_create_leading_directories_const(name);\n+\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+}\n+\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid)\n+{\n+\treturn odb_loose_path(r->objects->odb, buf, oid);\n+}\n+\n+/*\n+ * Return non-zero iff the path is usable as an alternate object database.\n+ */\n+static int alt_odb_usable(struct raw_object_store *o,\n+\t\t\t  struct strbuf *path,\n+\t\t\t  const char *normalized_objdir, khiter_t *pos)\n+{\n+\tint r;\n+\n+\t/* Detect cases where alternate disappeared */\n+\tif (!is_directory(path->buf)) {\n+\t\terror(_(\"object directory %s does not exist; \"\n+\t\t\t\"check .git/objects/info/alternates\"),\n+\t\t      path->buf);\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Prevent the common mistake of listing the same\n+\t * thing twice, or object directory itself.\n+\t */\n+\tif (!o->odb_by_path) {\n+\t\tkhiter_t p;\n+\n+\t\to->odb_by_path = kh_init_odb_path_map();\n+\t\tassert(!o->odb->next);\n+\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n+\t\tassert(r == 1); /* never used */\n+\t\tkh_value(o->odb_by_path, p) = o->odb;\n+\t}\n+\tif (fspatheq(path->buf, normalized_objdir))\n+\t\treturn 0;\n+\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n+\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n+\treturn r == 0 ? 0 : 1;\n+}\n+\n+/*\n+ * Prepare alternate object database registry.\n+ *\n+ * The variable alt_odb_list points at the list of struct\n+ * object_directory.  The elements on this list come from\n+ * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n+ * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n+ * whose contents is similar to that environment variable but can be\n+ * LF separated.  Its base points at a statically allocated buffer that\n+ * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n+ * its name points just after the slash at the end of \".git/objects/\"\n+ * in the example above, and has enough space to hold all hex characters\n+ * of the object ID, an extra slash for the first level indirection, and\n+ * the terminating NUL.\n+ */\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth);\n+static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n+\tconst char *relative_base, int depth, const char *normalized_objdir)\n+{\n+\tstruct object_directory *ent;\n+\tstruct strbuf pathbuf = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tkhiter_t pos;\n+\tint ret = -1;\n+\n+\tif (!is_absolute_path(entry->buf) && relative_base) {\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n+\t\tstrbuf_addch(&pathbuf, '/');\n+\t}\n+\tstrbuf_addbuf(&pathbuf, entry);\n+\n+\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n+\t\terror(_(\"unable to normalize alternate object path: %s\"),\n+\t\t      pathbuf.buf);\n+\t\tgoto error;\n+\t}\n+\tstrbuf_swap(&pathbuf, &tmp);\n+\n+\t/*\n+\t * The trailing slash after the directory name is given by\n+\t * this function at the end. Remove duplicates.\n+\t */\n+\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n+\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n+\n+\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n+\t\tgoto error;\n+\n+\tCALLOC_ARRAY(ent, 1);\n+\t/* pathbuf.buf is already in r->objects->odb_by_path */\n+\tent->path = strbuf_detach(&pathbuf, NULL);\n+\n+\t/* add the alternate entry */\n+\t*r->objects->odb_tail = ent;\n+\tr->objects->odb_tail = &(ent->next);\n+\tent->next = NULL;\n+\tassert(r->objects->odb_by_path);\n+\tkh_value(r->objects->odb_by_path, pos) = ent;\n+\n+\t/* recursively add alternates */\n+\tread_info_alternates(r, ent->path, depth + 1);\n+\tret = 0;\n+ error:\n+\tstrbuf_release(&tmp);\n+\tstrbuf_release(&pathbuf);\n+\treturn ret;\n+}\n+\n+static const char *parse_alt_odb_entry(const char *string,\n+\t\t\t\t       int sep,\n+\t\t\t\t       struct strbuf *out)\n+{\n+\tconst char *end;\n+\n+\tstrbuf_reset(out);\n+\n+\tif (*string == '#') {\n+\t\t/* comment; consume up to next separator */\n+\t\tend = strchrnul(string, sep);\n+\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n+\t\t/*\n+\t\t * quoted path; unquote_c_style has copied the\n+\t\t * data for us and set \"end\". Broken quoting (e.g.,\n+\t\t * an entry that doesn't end with a quote) falls\n+\t\t * back to the unquoted case below.\n+\t\t */\n+\t} else {\n+\t\t/* normal, unquoted path */\n+\t\tend = strchrnul(string, sep);\n+\t\tstrbuf_add(out, string, end - string);\n+\t}\n+\n+\tif (*end)\n+\t\tend++;\n+\treturn end;\n+}\n+\n+static void link_alt_odb_entries(struct repository *r, const char *alt,\n+\t\t\t\t int sep, const char *relative_base, int depth)\n+{\n+\tstruct strbuf objdirbuf = STRBUF_INIT;\n+\tstruct strbuf entry = STRBUF_INIT;\n+\n+\tif (!alt || !*alt)\n+\t\treturn;\n+\n+\tif (depth > 5) {\n+\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n+\t\t\t\trelative_base);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n+\n+\twhile (*alt) {\n+\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n+\t\tif (!entry.len)\n+\t\t\tcontinue;\n+\t\tlink_alt_odb_entry(r, &entry,\n+\t\t\t\t   relative_base, depth, objdirbuf.buf);\n+\t}\n+\tstrbuf_release(&entry);\n+\tstrbuf_release(&objdirbuf);\n+}\n+\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth)\n+{\n+\tchar *path;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n+\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n+\t\twarn_on_fopen_errors(path);\n+\t\tfree(path);\n+\t\treturn;\n+\t}\n+\n+\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n+\tstrbuf_release(&buf);\n+\tfree(path);\n+}\n+\n+void add_to_alternates_file(const char *reference)\n+{\n+\tstruct lock_file lock = LOCK_INIT;\n+\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n+\tFILE *in, *out;\n+\tint found = 0;\n+\n+\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n+\tout = fdopen_lock_file(&lock, \"w\");\n+\tif (!out)\n+\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n+\n+\tin = fopen(alts, \"r\");\n+\tif (in) {\n+\t\tstruct strbuf line = STRBUF_INIT;\n+\n+\t\twhile (strbuf_getline(&line, in) != EOF) {\n+\t\t\tif (!strcmp(reference, line.buf)) {\n+\t\t\t\tfound = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n+\t\t}\n+\n+\t\tstrbuf_release(&line);\n+\t\tfclose(in);\n+\t}\n+\telse if (errno != ENOENT)\n+\t\tdie_errno(_(\"unable to read alternates file\"));\n+\n+\tif (found) {\n+\t\trollback_lock_file(&lock);\n+\t} else {\n+\t\tfprintf_or_die(out, \"%s\\n\", reference);\n+\t\tif (commit_lock_file(&lock))\n+\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n+\t\tif (the_repository->objects->loaded_alternates)\n+\t\t\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t\t\t     '\\n', NULL, 0);\n+\t}\n+\tfree(alts);\n+}\n+\n+void add_to_alternates_memory(const char *reference)\n+{\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t     '\\n', NULL, 0);\n+}\n+\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n+{\n+\tstruct object_directory *new_odb;\n+\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\t/*\n+\t * Make a new primary odb and link the old primary ODB in as an\n+\t * alternate\n+\t */\n+\tnew_odb = xcalloc(1, sizeof(*new_odb));\n+\tnew_odb->path = xstrdup(dir);\n+\n+\t/*\n+\t * Disable ref updates while a temporary odb is active, since\n+\t * the objects in the database may roll back.\n+\t */\n+\tnew_odb->disable_ref_updates = 1;\n+\tnew_odb->will_destroy = will_destroy;\n+\tnew_odb->next = the_repository->objects->odb;\n+\tthe_repository->objects->odb = new_odb;\n+\treturn new_odb->next;\n+}\n+\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n+{\n+\tstruct object_directory *cur_odb = the_repository->objects->odb;\n+\n+\tif (strcmp(old_path, cur_odb->path))\n+\t\tBUG(\"expected %s as primary object store; found %s\",\n+\t\t    old_path, cur_odb->path);\n+\n+\tif (cur_odb->next != restore_odb)\n+\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n+\n+\tthe_repository->objects->odb = restore_odb;\n+\tfree_object_directory(cur_odb);\n+}\n+\n+/*\n+ * Compute the exact path an alternate is at and returns it. In case of\n+ * error NULL is returned and the human readable error is added to `err`\n+ * `path` may be relative and should point to $GIT_DIR.\n+ * `err` must not be null.\n+ */\n+char *compute_alternate_path(const char *path, struct strbuf *err)\n+{\n+\tchar *ref_git = NULL;\n+\tconst char *repo;\n+\tint seen_error = 0;\n+\n+\tref_git = real_pathdup(path, 0);\n+\tif (!ref_git) {\n+\t\tseen_error = 1;\n+\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\trepo = read_gitfile(ref_git);\n+\tif (!repo)\n+\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n+\tif (repo) {\n+\t\tfree(ref_git);\n+\t\tref_git = xstrdup(repo);\n+\t}\n+\n+\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n+\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n+\t\tfree(ref_git);\n+\t\tref_git = ref_git_git;\n+\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tseen_error = 1;\n+\t\tif (get_common_dir(&sb, ref_git)) {\n+\t\t\tstrbuf_addf(err,\n+\t\t\t\t    _(\"reference repository '%s' as a linked \"\n+\t\t\t\t      \"checkout is not supported yet.\"),\n+\t\t\t\t    path);\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n+\t\t\t\t\t\"local repository.\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err,\n+\t\t\t    _(\"reference repository '%s' is grafted\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+out:\n+\tif (seen_error) {\n+\t\tFREE_AND_NULL(ref_git);\n+\t}\n+\n+\treturn ref_git;\n+}\n+\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n+{\n+\tstruct object_directory *odb;\n+\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n+\tstruct strbuf odb_path_real = STRBUF_INIT;\n+\n+\tprepare_alt_odb(r);\n+\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n+\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n+\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n+\t\t\tbreak;\n+\t}\n+\n+\tfree(obj_dir_real);\n+\tstrbuf_release(&odb_path_real);\n+\n+\tif (!odb)\n+\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n+\treturn odb;\n+}\n+\n+static void fill_alternate_refs_command(struct child_process *cmd,\n+\t\t\t\t\tconst char *repo_path)\n+{\n+\tconst char *value;\n+\n+\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n+\t\tcmd->use_shell = 1;\n+\n+\t\tstrvec_push(&cmd->args, value);\n+\t\tstrvec_push(&cmd->args, repo_path);\n+\t} else {\n+\t\tcmd->git_cmd = 1;\n+\n+\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n+\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n+\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n+\n+\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n+\t\t\tstrvec_push(&cmd->args, \"--\");\n+\t\t\tstrvec_split(&cmd->args, value);\n+\t\t}\n+\t}\n+\n+\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n+\tcmd->out = -1;\n+}\n+\n+static void read_alternate_refs(const char *path,\n+\t\t\t\talternate_ref_fn *cb,\n+\t\t\t\tvoid *data)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tstruct strbuf line = STRBUF_INIT;\n+\tFILE *fh;\n+\n+\tfill_alternate_refs_command(&cmd, path);\n+\n+\tif (start_command(&cmd))\n+\t\treturn;\n+\n+\tfh = xfdopen(cmd.out, \"r\");\n+\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n+\t\tstruct object_id oid;\n+\t\tconst char *p;\n+\n+\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n+\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n+\t\t\t\tline.buf);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcb(&oid, data);\n+\t}\n+\n+\tfclose(fh);\n+\tfinish_command(&cmd);\n+\tstrbuf_release(&line);\n+}\n+\n+struct alternate_refs_data {\n+\talternate_ref_fn *fn;\n+\tvoid *data;\n+};\n+\n+static int refs_from_alternate_cb(struct object_directory *e,\n+\t\t\t\t  void *data)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tsize_t base_len;\n+\tstruct alternate_refs_data *cb = data;\n+\n+\tif (!strbuf_realpath(&path, e->path, 0))\n+\t\tgoto out;\n+\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n+\t\tgoto out;\n+\tbase_len = path.len;\n+\n+\t/* Is this a git repository with refs? */\n+\tstrbuf_addstr(&path, \"/refs\");\n+\tif (!is_directory(path.buf))\n+\t\tgoto out;\n+\tstrbuf_setlen(&path, base_len);\n+\n+\tread_alternate_refs(path.buf, cb->fn, cb->data);\n+\n+out:\n+\tstrbuf_release(&path);\n+\treturn 0;\n+}\n+\n+void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n+{\n+\tstruct alternate_refs_data cb;\n+\tcb.fn = fn;\n+\tcb.data = data;\n+\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n+}\n+\n+int foreach_alt_odb(alt_odb_fn fn, void *cb)\n+{\n+\tstruct object_directory *ent;\n+\tint r = 0;\n+\n+\tprepare_alt_odb(the_repository);\n+\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n+\t\tr = fn(ent, cb);\n+\t\tif (r)\n+\t\t\tbreak;\n+\t}\n+\treturn r;\n+}\n+\n+void prepare_alt_odb(struct repository *r)\n+{\n+\tif (r->objects->loaded_alternates)\n+\t\treturn;\n+\n+\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n+\n+\tread_info_alternates(r, r->objects->odb->path, 0);\n+\tr->objects->loaded_alternates = 1;\n+}\n+\n+int has_alt_odb(struct repository *r)\n+{\n+\tprepare_alt_odb(r);\n+\treturn !!r->objects->odb->next;\n+}\n+\n+int obj_read_use_lock = 0;\n+pthread_mutex_t obj_read_mutex;\n+\n+void enable_obj_read_lock(void)\n+{\n+\tif (obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 1;\n+\tinit_recursive_mutex(&obj_read_mutex);\n+}\n+\n+void disable_obj_read_lock(void)\n+{\n+\tif (!obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 0;\n+\tpthread_mutex_destroy(&obj_read_mutex);\n+}\n+\n+int fetch_if_missing = 1;\n+\n+static int do_oid_object_info_extended(struct repository *r,\n+\t\t\t\t       const struct object_id *oid,\n+\t\t\t\t       struct object_info *oi, unsigned flags)\n+{\n+\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n+\tconst struct cached_object *co;\n+\tstruct pack_entry e;\n+\tint rtype;\n+\tconst struct object_id *real = oid;\n+\tint already_retried = 0;\n+\n+\n+\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n+\t\treal = lookup_replace_object(r, oid);\n+\n+\tif (is_null_oid(real))\n+\t\treturn -1;\n+\n+\tif (!oi)\n+\t\toi = &blank_oi;\n+\n+\tco = find_cached_object(real);\n+\tif (co) {\n+\t\tif (oi->typep)\n+\t\t\t*(oi->typep) = co->type;\n+\t\tif (oi->sizep)\n+\t\t\t*(oi->sizep) = co->size;\n+\t\tif (oi->disk_sizep)\n+\t\t\t*(oi->disk_sizep) = 0;\n+\t\tif (oi->delta_base_oid)\n+\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n+\t\tif (oi->type_name)\n+\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n+\t\tif (oi->contentp)\n+\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n+\t\toi->whence = OI_CACHED;\n+\t\treturn 0;\n+\t}\n+\n+\twhile (1) {\n+\t\tif (find_pack_entry(r, real, &e))\n+\t\t\tbreak;\n+\n+\t\t/* Most likely it's a loose object. */\n+\t\tif (!loose_object_info(r, real, oi, flags))\n+\t\t\treturn 0;\n+\n+\t\t/* Not a loose object; someone else may have just packed it. */\n+\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n+\t\t\treprepare_packed_git(r);\n+\t\t\tif (find_pack_entry(r, real, &e))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If r is the_repository, this might be an attempt at\n+\t\t * accessing a submodule object as if it were in the_repository\n+\t\t * (having called add_submodule_odb() on that submodule's ODB).\n+\t\t * If any such ODBs exist, register them and try again.\n+\t\t */\n+\t\tif (r == the_repository &&\n+\t\t    register_all_submodule_odb_as_alternates())\n+\t\t\t/* We added some alternates; retry */\n+\t\t\tcontinue;\n+\n+\t\t/* Check if it is a missing object */\n+\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n+\t\t    !already_retried &&\n+\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n+\t\t\tpromisor_remote_get_direct(r, real, 1);\n+\t\t\talready_retried = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n+\t\t\tconst struct packed_git *p;\n+\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n+\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n+\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n+\t\t\tif ((p = has_packed_and_bad(r, real)))\n+\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n+\t\t\t\t    oid_to_hex(real), p->pack_name);\n+\t\t}\n+\t\treturn -1;\n+\t}\n+\n+\tif (oi == &blank_oi)\n+\t\t/*\n+\t\t * We know that the caller doesn't actually need the\n+\t\t * information below, so return early.\n+\t\t */\n+\t\treturn 0;\n+\trtype = packed_object_info(r, e.p, e.offset, oi);\n+\tif (rtype < 0) {\n+\t\tmark_bad_packed_object(e.p, real);\n+\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n+\t} else if (oi->whence == OI_PACKED) {\n+\t\toi->u.packed.offset = e.offset;\n+\t\toi->u.packed.pack = e.p;\n+\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n+\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int oid_object_info_convert(struct repository *r,\n+\t\t\t\t   const struct object_id *input_oid,\n+\t\t\t\t   struct object_info *input_oi, unsigned flags)\n+{\n+\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n+\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n+\tstruct strbuf type_name = STRBUF_INIT;\n+\tstruct object_id oid, delta_base_oid;\n+\tstruct object_info new_oi, *oi;\n+\tunsigned long size;\n+\tvoid *content;\n+\tint ret;\n+\n+\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n+\t\tif (do_die)\n+\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Is new_oi needed? */\n+\toi = input_oi;\n+\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n+\t\t\t input_oi->contentp)) {\n+\t\tnew_oi = *input_oi;\n+\t\t/* Does delta_base_oid need to be converted? */\n+\t\tif (input_oi->delta_base_oid)\n+\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n+\t\t/* Will the attributes differ when converted? */\n+\t\tif (input_oi->sizep || input_oi->contentp) {\n+\t\t\tnew_oi.contentp = &content;\n+\t\t\tnew_oi.sizep = &size;\n+\t\t\tnew_oi.type_name = &type_name;\n+\t\t}\n+\t\toi = &new_oi;\n+\t}\n+\n+\tret = oid_object_info_extended(r, &oid, oi, flags);\n+\tif (ret)\n+\t\treturn -1;\n+\tif (oi == input_oi)\n+\t\treturn ret;\n+\n+\tif (new_oi.contentp) {\n+\t\tstruct strbuf outbuf = STRBUF_INIT;\n+\t\tenum object_type type;\n+\n+\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n+\t\t\t\t\t       !do_die);\n+\t\tif (type == -1)\n+\t\t\treturn -1;\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = convert_object_file(the_repository, &outbuf,\n+\t\t\t\t\t\t  the_hash_algo, input_algo,\n+\t\t\t\t\t\t  content, size, type, !do_die);\n+\t\t\tfree(content);\n+\t\t\tif (ret == -1)\n+\t\t\t\treturn -1;\n+\t\t\tsize = outbuf.len;\n+\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n+\t\t}\n+\t\tif (input_oi->sizep)\n+\t\t\t*input_oi->sizep = size;\n+\t\tif (input_oi->contentp)\n+\t\t\t*input_oi->contentp = content;\n+\t\telse\n+\t\t\tfree(content);\n+\t\tif (input_oi->type_name)\n+\t\t\t*input_oi->type_name = type_name;\n+\t\telse\n+\t\t\tstrbuf_release(&type_name);\n+\t}\n+\tif (new_oi.delta_base_oid == &delta_base_oid) {\n+\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n+\t\t\t\t input_oi->delta_base_oid)) {\n+\t\t\tif (do_die)\n+\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t\t    oid_to_hex(&delta_base_oid),\n+\t\t\t\t    input_algo->name);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\tinput_oi->whence = new_oi.whence;\n+\tinput_oi->u = new_oi.u;\n+\treturn ret;\n+}\n+\n+int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n+\t\t\t     struct object_info *oi, unsigned flags)\n+{\n+\tint ret;\n+\n+\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n+\t\treturn oid_object_info_convert(r, oid, oi, flags);\n+\n+\tobj_read_lock();\n+\tret = do_oid_object_info_extended(r, oid, oi, flags);\n+\tobj_read_unlock();\n+\treturn ret;\n+}\n+\n+\n+/* returns enum object_type or negative */\n+int oid_object_info(struct repository *r,\n+\t\t    const struct object_id *oid,\n+\t\t    unsigned long *sizep)\n+{\n+\tenum object_type type;\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\n+\toi.typep = &type;\n+\toi.sizep = sizep;\n+\tif (oid_object_info_extended(r, oid, &oi,\n+\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n+\t\treturn -1;\n+\treturn type;\n+}\n+\n+int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid)\n+{\n+\tstruct cached_object_entry *co;\n+\tchar *co_buf;\n+\n+\thash_object_file(the_hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(oid))\n+\t\treturn 0;\n+\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n+\tco = &cached_objects[cached_object_nr++];\n+\tco->value.size = len;\n+\tco->value.type = type;\n+\tco_buf = xmalloc(len);\n+\tmemcpy(co_buf, buf, len);\n+\tco->value.buf = co_buf;\n+\toidcpy(&co->oid, oid);\n+\treturn 0;\n+}\n+\n+/*\n+ * This function dies on corrupt objects; the callers who want to\n+ * deal with them should arrange to call oid_object_info_extended() and give\n+ * error messages themselves.\n+ */\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size)\n+{\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n+\tvoid *data;\n+\n+\toi.typep = type;\n+\toi.sizep = size;\n+\toi.contentp = &data;\n+\tif (oid_object_info_extended(r, oid, &oi, flags))\n+\t\treturn NULL;\n+\n+\treturn data;\n+}\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *actual_oid_return)\n+{\n+\tenum object_type type;\n+\tvoid *buffer;\n+\tunsigned long isize;\n+\tstruct object_id actual_oid;\n+\n+\toidcpy(&actual_oid, oid);\n+\twhile (1) {\n+\t\tint ref_length = -1;\n+\t\tconst char *ref_type = NULL;\n+\n+\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n+\t\tif (!buffer)\n+\t\t\treturn NULL;\n+\t\tif (type == required_type) {\n+\t\t\t*size = isize;\n+\t\t\tif (actual_oid_return)\n+\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n+\t\t\treturn buffer;\n+\t\t}\n+\t\t/* Handle references */\n+\t\telse if (type == OBJ_COMMIT)\n+\t\t\tref_type = \"tree \";\n+\t\telse if (type == OBJ_TAG)\n+\t\t\tref_type = \"object \";\n+\t\telse {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tref_length = strlen(ref_type);\n+\n+\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n+\t\t    memcmp(buffer, ref_type, ref_length) ||\n+\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tfree(buffer);\n+\t\t/* Now we have the ID of the referred-to object in\n+\t\t * actual_oid.  Check again. */\n+\t}\n+}\n+\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags)\n+{\n+\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n+\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n+\t\t(quick ? OBJECT_INFO_QUICK : 0);\n+\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n+}\n+\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags)\n+{\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n+}\n+\n+int repo_has_object_file(struct repository *r,\n+\t\t\t const struct object_id *oid)\n+{\n+\treturn repo_has_object_file_with_flags(r, oid, 0);\n+}\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect)\n+{\n+\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n+\tif (type < 0)\n+\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n+\tif (type != expect)\n+\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n+\t\t    type_name(expect));\n+}\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515984","messageId":"20250411-pks-split-object-file-v2-5-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 5/9] object-file: split up concerns of `HASH_*` flags","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:54Z","receivedAt":"2025-04-11T09:30:04Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The functions `hash_object_file()`, `write_object_file()` and\n`index_fd()` reuse the same set of flags to alter their behaviour. This\nnot only adds confusion, but given that every function only supports a\nsubset of the flags it becomes very hard to see which flags can be\npassed to what function. Last but not least, this entangles the\nimplementation of all three function families.\n\nSplit up concerns by creating separate flags for each of the function\nfamilies.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/hash-object.c  | 23 +++++++++++++++++------\n builtin/replace.c      |  2 +-\n builtin/update-index.c |  2 +-\n bulk-checkin.c         |  6 +++---\n cache-tree.c           |  2 +-\n notes-merge.c          |  2 +-\n object-file.c          | 18 +++++++++---------\n object-file.h          | 25 +++++++++++++++++++++----\n read-cache.c           |  4 ++--\n 9 files changed, 56 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex a25f0403f44..e7c0d6afdef 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -19,6 +19,11 @@\n #include \"strbuf.h\"\n #include \"write-or-die.h\"\n \n+enum {\n+\tHASH_OBJECT_CHECK = (1 << 0),\n+\tHASH_OBJECT_WRITE = (1 << 1),\n+};\n+\n /*\n  * This is to create corrupt objects for debugging and as such it\n  * needs to bypass the data conversion performed by, and the type\n@@ -33,7 +38,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n \t\tret = -1;\n \telse\n \t\tret = write_object_file_literally(buf.buf, buf.len, type, oid,\n-\t\t\t\t\t\t flags);\n+\t\t\t\t\t\t  (flags & HASH_OBJECT_WRITE) ? WRITE_OBJECT_FILE_PERSIST : 0);\n \tclose(fd);\n \tstrbuf_release(&buf);\n \treturn ret;\n@@ -42,15 +47,21 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n \t\t    int literally)\n {\n+\tunsigned int index_flags = 0;\n \tstruct stat st;\n \tstruct object_id oid;\n \n+\tif (flags & HASH_OBJECT_WRITE)\n+\t\tindex_flags |= INDEX_WRITE_OBJECT;\n+\tif (flags & HASH_OBJECT_CHECK)\n+\t\tindex_flags |= INDEX_FORMAT_CHECK;\n+\n \tif (fstat(fd, &st) < 0 ||\n \t    (literally\n \t     ? hash_literally(&oid, fd, type, flags)\n \t     : index_fd(the_repository->index, &oid, fd, &st,\n-\t\t\ttype_from_string(type), path, flags)))\n-\t\tdie((flags & HASH_WRITE_OBJECT)\n+\t\t\ttype_from_string(type), path, index_flags)))\n+\t\tdie((flags & HASH_OBJECT_WRITE)\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n \tprintf(\"%s\\n\", oid_to_hex(&oid));\n@@ -102,13 +113,13 @@ int cmd_hash_object(int argc,\n \tint no_filters = 0;\n \tint literally = 0;\n \tint nongit = 0;\n-\tunsigned flags = HASH_FORMAT_CHECK;\n+\tunsigned flags = HASH_OBJECT_CHECK;\n \tconst char *vpath = NULL;\n \tchar *vpath_free = NULL;\n \tconst struct option hash_object_options[] = {\n \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n-\t\t\tHASH_WRITE_OBJECT),\n+\t\t\tHASH_OBJECT_WRITE),\n \t\tOPT_COUNTUP( 0 , \"stdin\", &hashstdin, N_(\"read the object from stdin\")),\n \t\tOPT_BOOL( 0 , \"stdin-paths\", &stdin_paths, N_(\"read file names from stdin\")),\n \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n@@ -122,7 +133,7 @@ int cmd_hash_object(int argc,\n \targc = parse_options(argc, argv, prefix, hash_object_options,\n \t\t\t     hash_object_usage, 0);\n \n-\tif (flags & HASH_WRITE_OBJECT)\n+\tif (flags & HASH_OBJECT_WRITE)\n \t\tprefix = setup_git_directory();\n \telse\n \t\tprefix = setup_git_directory_gently(&nongit);\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 15ec0922ce1..2b4fc9a68b3 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -305,7 +305,7 @@ static int import_object(struct object_id *oid, enum object_type type,\n \t\tstrbuf_release(&result);\n \t} else {\n \t\tstruct stat st;\n-\t\tint flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;\n+\t\tint flags = INDEX_FORMAT_CHECK | INDEX_WRITE_OBJECT;\n \n \t\tif (fstat(fd, &st) < 0) {\n \t\t\terror_errno(_(\"unable to fstat %s\"), filename);\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex b2f6b1a3fbb..f0cf964294d 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -304,7 +304,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len\n \tce->ce_mode = ce_mode_from_stat(old, st->st_mode);\n \n \tif (index_path(the_repository->index, &ce->oid, path, st,\n-\t\t       info_only ? 0 : HASH_WRITE_OBJECT)) {\n+\t\t       info_only ? 0 : INDEX_WRITE_OBJECT)) {\n \t\tdiscard_cache_entry(ce);\n \t\treturn -1;\n \t}\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 23ac00ea0a6..309201a76a6 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -171,7 +171,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n \tunsigned char obuf[16384];\n \tunsigned hdrlen;\n \tint status = Z_OK;\n-\tint write_object = (flags & HASH_WRITE_OBJECT);\n+\tint write_object = (flags & INDEX_WRITE_OBJECT);\n \toff_t offset = 0;\n \n \tgit_deflate_init(&s, pack_compression_level);\n@@ -241,7 +241,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n static void prepare_to_stream(struct bulk_checkin_packfile *state,\n \t\t\t      unsigned flags)\n {\n-\tif (!(flags & HASH_WRITE_OBJECT) || state->f)\n+\tif (!(flags & INDEX_WRITE_OBJECT) || state->f)\n \t\treturn;\n \n \tstate->f = create_tmp_packfile(the_repository, &state->pack_tmp_name);\n@@ -275,7 +275,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \tgit_hash_update(&ctx, obuf, header_len);\n \n \t/* Note: idx is non-NULL when we are writing */\n-\tif ((flags & HASH_WRITE_OBJECT) != 0) {\n+\tif ((flags & INDEX_WRITE_OBJECT) != 0) {\n \t\tCALLOC_ARRAY(idx, 1);\n \n \t\tprepare_to_stream(state, flags);\ndiff --git a/cache-tree.c b/cache-tree.c\nindex bcbcad3d61a..4c8167ea927 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -452,7 +452,7 @@ static int update_one(struct cache_tree *it,\n \t\t\t\t OBJ_TREE, &it->oid);\n \t} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,\n \t\t\t\t\t   &it->oid, NULL, flags & WRITE_TREE_SILENT\n-\t\t\t\t\t   ? HASH_SILENT : 0)) {\n+\t\t\t\t\t   ? WRITE_OBJECT_FILE_SILENT : 0)) {\n \t\tstrbuf_release(&buffer);\n \t\treturn -1;\n \t}\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 5008faef450..d9f2914c819 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -729,7 +729,7 @@ int notes_merge_commit(struct notes_merge_options *o,\n \t\t/* write file as blob, and add to partial_tree */\n \t\tif (stat(path.buf, &st))\n \t\t\tdie_errno(\"Failed to stat '%s'\", path.buf);\n-\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))\n+\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, INDEX_WRITE_OBJECT))\n \t\t\tdie(\"Failed to write blob object from '%s'\", path.buf);\n \t\tif (add_note(partial_tree, &obj_oid, &blob_oid, NULL))\n \t\t\tdie(\"Failed to add resolved note '%s' to notes tree\",\ndiff --git a/object-file.c b/object-file.c\nindex 1c74aa239fb..dea5a70a331 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -33,9 +33,9 @@\n \n static int get_conv_flags(unsigned flags)\n {\n-\tif (flags & HASH_RENORMALIZE)\n+\tif (flags & INDEX_RENORMALIZE)\n \t\treturn CONV_EOL_RENORMALIZE;\n-\telse if (flags & HASH_WRITE_OBJECT)\n+\telse if (flags & INDEX_WRITE_OBJECT)\n \t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n \telse\n \t\treturn 0;\n@@ -835,7 +835,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n \n \tfd = create_tmpfile(tmp_file, filename);\n \tif (fd < 0) {\n-\t\tif (flags & HASH_SILENT)\n+\t\tif (flags & WRITE_OBJECT_FILE_SILENT)\n \t\t\treturn -1;\n \t\telse if (errno == EACCES)\n \t\t\treturn error(_(\"insufficient permission for adding \"\n@@ -967,7 +967,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,\n \t\tutb.actime = mtime;\n \t\tutb.modtime = mtime;\n \t\tif (utime(tmp_file.buf, &utb) < 0 &&\n-\t\t    !(flags & HASH_SILENT))\n+\t\t    !(flags & WRITE_OBJECT_FILE_SILENT))\n \t\t\twarning_errno(_(\"failed utime() on %s\"), tmp_file.buf);\n \t}\n \n@@ -1178,7 +1178,7 @@ int write_object_file_literally(const void *buf, unsigned long len,\n \twrite_object_file_prepare_literally(the_hash_algo, buf, len, type,\n \t\t\t\t\t    oid, header, &hdrlen);\n \n-\tif (!(flags & HASH_WRITE_OBJECT))\n+\tif (!(flags & WRITE_OBJECT_FILE_PERSIST))\n \t\tgoto cleanup;\n \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n \t\tgoto cleanup;\n@@ -1249,7 +1249,7 @@ static int index_mem(struct index_state *istate,\n {\n \tstruct strbuf nbuf = STRBUF_INIT;\n \tint ret = 0;\n-\tint write_object = flags & HASH_WRITE_OBJECT;\n+\tint write_object = flags & INDEX_WRITE_OBJECT;\n \n \tif (!type)\n \t\ttype = OBJ_BLOB;\n@@ -1264,7 +1264,7 @@ static int index_mem(struct index_state *istate,\n \t\t\tsize = nbuf.len;\n \t\t}\n \t}\n-\tif (flags & HASH_FORMAT_CHECK) {\n+\tif (flags & INDEX_FORMAT_CHECK) {\n \t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n \n \t\topts.strict = 1;\n@@ -1290,7 +1290,7 @@ static int index_stream_convert_blob(struct index_state *istate,\n \t\t\t\t     unsigned flags)\n {\n \tint ret = 0;\n-\tconst int write_object = flags & HASH_WRITE_OBJECT;\n+\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n \tstruct strbuf sbuf = STRBUF_INIT;\n \n \tassert(path);\n@@ -1422,7 +1422,7 @@ int index_path(struct index_state *istate, struct object_id *oid,\n \tcase S_IFLNK:\n \t\tif (strbuf_readlink(&sb, path, st->st_size))\n \t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n-\t\tif (!(flags & HASH_WRITE_OBJECT))\n+\t\tif (!(flags & INDEX_WRITE_OBJECT))\n \t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n \t\t\t\t\t OBJ_BLOB, oid);\n \t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\ndiff --git a/object-file.h b/object-file.h\nindex 78c84d970a9..c002fbe2345 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -14,10 +14,12 @@ struct index_state;\n  */\n extern int fetch_if_missing;\n \n-#define HASH_WRITE_OBJECT 1\n-#define HASH_FORMAT_CHECK 2\n-#define HASH_RENORMALIZE  4\n-#define HASH_SILENT 8\n+enum {\n+\tINDEX_WRITE_OBJECT = (1 << 0),\n+\tINDEX_FORMAT_CHECK = (1 << 1),\n+\tINDEX_RENORMALIZE  = (1 << 2),\n+};\n+\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n@@ -84,6 +86,21 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+enum {\n+\t/*\n+\t * By default, `write_object_file_literally()` does not actually write\n+\t * anything into the object store, but only computes the object ID.\n+\t * This flag changes that so that the object will be written as a loose\n+\t * object and persisted.\n+\t */\n+\tWRITE_OBJECT_FILE_PERSIST = (1 << 0),\n+\n+\t/*\n+\t * Do not print an error in case something gose wrong.\n+\t */\n+\tWRITE_OBJECT_FILE_SILENT = (1 << 1),\n+};\n+\n int write_object_file_flags(const void *buf, unsigned long len,\n \t\t\t    enum object_type type, struct object_id *oid,\n \t\t\t    struct object_id *comapt_oid_in, unsigned flags);\ndiff --git a/read-cache.c b/read-cache.c\nindex 2f9e21c897d..23028f43a11 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -706,11 +706,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \tint intent_only = flags & ADD_CACHE_INTENT;\n \tint add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|\n \t\t\t  (intent_only ? ADD_CACHE_NEW_ONLY : 0));\n-\tunsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;\n+\tunsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;\n \tstruct object_id oid;\n \n \tif (flags & ADD_CACHE_RENORMALIZE)\n-\t\thash_flags |= HASH_RENORMALIZE;\n+\t\thash_flags |= INDEX_RENORMALIZE;\n \n \tif (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))\n \t\treturn error(_(\"%s: can only add regular files, symbolic links or git-directories\"), path);\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515986","messageId":"20250411-pks-split-object-file-v2-6-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 6/9] object-file: split out functions relating to index subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:55Z","receivedAt":"2025-04-11T09:30:06Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Split out functions relating to the index subsystem from \"object-file.c\"\nto help us separate concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/difftool.c    |   2 +-\n builtin/hash-object.c |   1 +\n builtin/replace.c     |   1 +\n bulk-checkin.c        |   1 +\n diff.c                |   2 +-\n notes-merge.c         |   1 +\n object-file.c         | 225 --------------------------------------------------\n object-file.h         |  11 ---\n read-cache.c          | 224 +++++++++++++++++++++++++++++++++++++++++++++++++\n read-cache.h          |   9 ++\n 10 files changed, 239 insertions(+), 238 deletions(-)\n\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 41cd00066cc..45d6ea8a801 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -22,7 +22,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"parse-options.h\"\n-#include \"read-cache-ll.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"sparse-index.h\"\n #include \"strvec.h\"\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex e7c0d6afdef..7aa889b1b4a 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -15,6 +15,7 @@\n #include \"blob.h\"\n #include \"quote.h\"\n #include \"parse-options.h\"\n+#include \"read-cache.h\"\n #include \"setup.h\"\n #include \"strbuf.h\"\n #include \"write-or-die.h\"\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 2b4fc9a68b3..ce2948a9b2a 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -20,6 +20,7 @@\n #include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n+#include \"read-cache.h\"\n #include \"replace-object.h\"\n #include \"tag.h\"\n #include \"wildmatch.h\"\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 309201a76a6..0b9bad92868 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -11,6 +11,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"csum-file.h\"\n #include \"pack.h\"\ndiff --git a/diff.c b/diff.c\nindex 3bcf5028831..c361199ab1b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -42,7 +42,7 @@\n #include \"dir.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"read-cache-ll.h\"\n+#include \"read-cache.h\"\n #include \"setup.h\"\n #include \"strmap.h\"\n #include \"ws.h\"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex d9f2914c819..13750490eff 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -10,6 +10,7 @@\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\n+#include \"read-cache.h\"\n #include \"repository.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\ndiff --git a/object-file.c b/object-file.c\nindex dea5a70a331..235d29858ff 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -12,9 +12,7 @@\n \n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n-#include \"convert.h\"\n #include \"environment.h\"\n-#include \"fsck.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"loose.h\"\n@@ -25,22 +23,11 @@\n #include \"pack.h\"\n #include \"packfile.h\"\n #include \"path.h\"\n-#include \"setup.h\"\n #include \"streaming.h\"\n \n /* The maximum size for an object header. */\n #define MAX_HEADER_LEN 32\n \n-static int get_conv_flags(unsigned flags)\n-{\n-\tif (flags & INDEX_RENORMALIZE)\n-\t\treturn CONV_EOL_RENORMALIZE;\n-\telse if (flags & INDEX_WRITE_OBJECT)\n-\t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n-\telse\n-\t\treturn 0;\n-}\n-\n static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n {\n \tint i;\n@@ -1225,218 +1212,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)\n \treturn ret;\n }\n \n-/*\n- * We can't use the normal fsck_error_function() for index_mem(),\n- * because we don't yet have a valid oid for it to report. Instead,\n- * report the minimal fsck error here, and rely on the caller to\n- * give more context.\n- */\n-static int hash_format_check_report(struct fsck_options *opts UNUSED,\n-\t\t\t\t    void *fsck_report UNUSED,\n-\t\t\t\t    enum fsck_msg_type msg_type UNUSED,\n-\t\t\t\t    enum fsck_msg_id msg_id UNUSED,\n-\t\t\t\t    const char *message)\n-{\n-\terror(_(\"object fails fsck: %s\"), message);\n-\treturn 1;\n-}\n-\n-static int index_mem(struct index_state *istate,\n-\t\t     struct object_id *oid,\n-\t\t     const void *buf, size_t size,\n-\t\t     enum object_type type,\n-\t\t     const char *path, unsigned flags)\n-{\n-\tstruct strbuf nbuf = STRBUF_INIT;\n-\tint ret = 0;\n-\tint write_object = flags & INDEX_WRITE_OBJECT;\n-\n-\tif (!type)\n-\t\ttype = OBJ_BLOB;\n-\n-\t/*\n-\t * Convert blobs to git internal format\n-\t */\n-\tif ((type == OBJ_BLOB) && path) {\n-\t\tif (convert_to_git(istate, path, buf, size, &nbuf,\n-\t\t\t\t   get_conv_flags(flags))) {\n-\t\t\tbuf = nbuf.buf;\n-\t\t\tsize = nbuf.len;\n-\t\t}\n-\t}\n-\tif (flags & INDEX_FORMAT_CHECK) {\n-\t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n-\n-\t\topts.strict = 1;\n-\t\topts.error_func = hash_format_check_report;\n-\t\tif (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts))\n-\t\t\tdie(_(\"refusing to create malformed object\"));\n-\t\tfsck_finish(&opts);\n-\t}\n-\n-\tif (write_object)\n-\t\tret = write_object_file(buf, size, type, oid);\n-\telse\n-\t\thash_object_file(the_hash_algo, buf, size, type, oid);\n-\n-\tstrbuf_release(&nbuf);\n-\treturn ret;\n-}\n-\n-static int index_stream_convert_blob(struct index_state *istate,\n-\t\t\t\t     struct object_id *oid,\n-\t\t\t\t     int fd,\n-\t\t\t\t     const char *path,\n-\t\t\t\t     unsigned flags)\n-{\n-\tint ret = 0;\n-\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n-\tstruct strbuf sbuf = STRBUF_INIT;\n-\n-\tassert(path);\n-\tassert(would_convert_to_git_filter_fd(istate, path));\n-\n-\tconvert_to_git_filter_fd(istate, path, fd, &sbuf,\n-\t\t\t\t get_conv_flags(flags));\n-\n-\tif (write_object)\n-\t\tret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,\n-\t\t\t\t\toid);\n-\telse\n-\t\thash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,\n-\t\t\t\t oid);\n-\tstrbuf_release(&sbuf);\n-\treturn ret;\n-}\n-\n-static int index_pipe(struct index_state *istate, struct object_id *oid,\n-\t\t      int fd, enum object_type type,\n-\t\t      const char *path, unsigned flags)\n-{\n-\tstruct strbuf sbuf = STRBUF_INIT;\n-\tint ret;\n-\n-\tif (strbuf_read(&sbuf, fd, 4096) >= 0)\n-\t\tret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);\n-\telse\n-\t\tret = -1;\n-\tstrbuf_release(&sbuf);\n-\treturn ret;\n-}\n-\n-#define SMALL_FILE_SIZE (32*1024)\n-\n-static int index_core(struct index_state *istate,\n-\t\t      struct object_id *oid, int fd, size_t size,\n-\t\t      enum object_type type, const char *path,\n-\t\t      unsigned flags)\n-{\n-\tint ret;\n-\n-\tif (!size) {\n-\t\tret = index_mem(istate, oid, \"\", size, type, path, flags);\n-\t} else if (size <= SMALL_FILE_SIZE) {\n-\t\tchar *buf = xmalloc(size);\n-\t\tssize_t read_result = read_in_full(fd, buf, size);\n-\t\tif (read_result < 0)\n-\t\t\tret = error_errno(_(\"read error while indexing %s\"),\n-\t\t\t\t\t  path ? path : \"<unknown>\");\n-\t\telse if (read_result != size)\n-\t\t\tret = error(_(\"short read while indexing %s\"),\n-\t\t\t\t    path ? path : \"<unknown>\");\n-\t\telse\n-\t\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n-\t\tfree(buf);\n-\t} else {\n-\t\tvoid *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n-\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n-\t\tmunmap(buf, size);\n-\t}\n-\treturn ret;\n-}\n-\n-/*\n- * This creates one packfile per large blob unless bulk-checkin\n- * machinery is \"plugged\".\n- *\n- * This also bypasses the usual \"convert-to-git\" dance, and that is on\n- * purpose. We could write a streaming version of the converting\n- * functions and insert that before feeding the data to fast-import\n- * (or equivalent in-core API described above). However, that is\n- * somewhat complicated, as we do not know the size of the filter\n- * result, which we need to know beforehand when writing a git object.\n- * Since the primary motivation for trying to stream from the working\n- * tree file and to avoid mmaping it in core is to deal with large\n- * binary blobs, they generally do not want to get any conversion, and\n- * callers should avoid this code path when filters are requested.\n- */\n-static int index_blob_stream(struct object_id *oid, int fd, size_t size,\n-\t\t\t     const char *path,\n-\t\t\t     unsigned flags)\n-{\n-\treturn index_blob_bulk_checkin(oid, fd, size, path, flags);\n-}\n-\n-int index_fd(struct index_state *istate, struct object_id *oid,\n-\t     int fd, struct stat *st,\n-\t     enum object_type type, const char *path, unsigned flags)\n-{\n-\tint ret;\n-\n-\t/*\n-\t * Call xsize_t() only when needed to avoid potentially unnecessary\n-\t * die() for large files.\n-\t */\n-\tif (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))\n-\t\tret = index_stream_convert_blob(istate, oid, fd, path, flags);\n-\telse if (!S_ISREG(st->st_mode))\n-\t\tret = index_pipe(istate, oid, fd, type, path, flags);\n-\telse if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) ||\n-\t\t type != OBJ_BLOB ||\n-\t\t (path && would_convert_to_git(istate, path)))\n-\t\tret = index_core(istate, oid, fd, xsize_t(st->st_size),\n-\t\t\t\t type, path, flags);\n-\telse\n-\t\tret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,\n-\t\t\t\t\tflags);\n-\tclose(fd);\n-\treturn ret;\n-}\n-\n-int index_path(struct index_state *istate, struct object_id *oid,\n-\t       const char *path, struct stat *st, unsigned flags)\n-{\n-\tint fd;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tint rc = 0;\n-\n-\tswitch (st->st_mode & S_IFMT) {\n-\tcase S_IFREG:\n-\t\tfd = open(path, O_RDONLY);\n-\t\tif (fd < 0)\n-\t\t\treturn error_errno(\"open(\\\"%s\\\")\", path);\n-\t\tif (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)\n-\t\t\treturn error(_(\"%s: failed to insert into database\"),\n-\t\t\t\t     path);\n-\t\tbreak;\n-\tcase S_IFLNK:\n-\t\tif (strbuf_readlink(&sb, path, st->st_size))\n-\t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n-\t\tif (!(flags & INDEX_WRITE_OBJECT))\n-\t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n-\t\t\t\t\t OBJ_BLOB, oid);\n-\t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\n-\t\t\trc = error(_(\"%s: failed to insert into database\"), path);\n-\t\tstrbuf_release(&sb);\n-\t\tbreak;\n-\tcase S_IFDIR:\n-\t\treturn repo_resolve_gitlink_ref(the_repository, path, \"HEAD\", oid);\n-\tdefault:\n-\t\treturn error(_(\"%s: unsupported file type\"), path);\n-\t}\n-\treturn rc;\n-}\n-\n int read_pack_header(int fd, struct pack_header *header)\n {\n \tif (read_in_full(fd, header, sizeof(*header)) != sizeof(*header))\ndiff --git a/object-file.h b/object-file.h\nindex c002fbe2345..7d48994f1b6 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -4,8 +4,6 @@\n #include \"git-zlib.h\"\n #include \"object.h\"\n \n-struct index_state;\n-\n /*\n  * Set this to 0 to prevent oid_object_info_extended() from fetching missing\n  * blobs. This has a difference only if extensions.partialClone is set.\n@@ -14,15 +12,6 @@ struct index_state;\n  */\n extern int fetch_if_missing;\n \n-enum {\n-\tINDEX_WRITE_OBJECT = (1 << 0),\n-\tINDEX_FORMAT_CHECK = (1 << 1),\n-\tINDEX_RENORMALIZE  = (1 << 2),\n-};\n-\n-int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n-int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n-\n struct object_directory;\n \n const char *odb_loose_path(struct object_directory *odb,\ndiff --git a/read-cache.c b/read-cache.c\nindex 23028f43a11..9909b56902f 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -10,9 +10,11 @@\n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n #include \"config.h\"\n+#include \"convert.h\"\n #include \"date.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n+#include \"fsck.h\"\n #include \"hex.h\"\n #include \"tempfile.h\"\n #include \"lockfile.h\"\n@@ -4007,3 +4009,225 @@ int add_files_to_cache(struct repository *repo, const char *prefix,\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n+\n+static int get_conv_flags(unsigned flags)\n+{\n+\tif (flags & INDEX_RENORMALIZE)\n+\t\treturn CONV_EOL_RENORMALIZE;\n+\telse if (flags & INDEX_WRITE_OBJECT)\n+\t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n+\telse\n+\t\treturn 0;\n+}\n+\n+/*\n+ * We can't use the normal fsck_error_function() for index_mem(),\n+ * because we don't yet have a valid oid for it to report. Instead,\n+ * report the minimal fsck error here, and rely on the caller to\n+ * give more context.\n+ */\n+static int hash_format_check_report(struct fsck_options *opts UNUSED,\n+\t\t\t\t    void *fsck_report UNUSED,\n+\t\t\t\t    enum fsck_msg_type msg_type UNUSED,\n+\t\t\t\t    enum fsck_msg_id msg_id UNUSED,\n+\t\t\t\t    const char *message)\n+{\n+\terror(_(\"object fails fsck: %s\"), message);\n+\treturn 1;\n+}\n+\n+static int index_mem(struct index_state *istate,\n+\t\t     struct object_id *oid,\n+\t\t     const void *buf, size_t size,\n+\t\t     enum object_type type,\n+\t\t     const char *path, unsigned flags)\n+{\n+\tstruct strbuf nbuf = STRBUF_INIT;\n+\tint ret = 0;\n+\tint write_object = flags & INDEX_WRITE_OBJECT;\n+\n+\tif (!type)\n+\t\ttype = OBJ_BLOB;\n+\n+\t/*\n+\t * Convert blobs to git internal format\n+\t */\n+\tif ((type == OBJ_BLOB) && path) {\n+\t\tif (convert_to_git(istate, path, buf, size, &nbuf,\n+\t\t\t\t   get_conv_flags(flags))) {\n+\t\t\tbuf = nbuf.buf;\n+\t\t\tsize = nbuf.len;\n+\t\t}\n+\t}\n+\tif (flags & INDEX_FORMAT_CHECK) {\n+\t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n+\n+\t\topts.strict = 1;\n+\t\topts.error_func = hash_format_check_report;\n+\t\tif (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts))\n+\t\t\tdie(_(\"refusing to create malformed object\"));\n+\t\tfsck_finish(&opts);\n+\t}\n+\n+\tif (write_object)\n+\t\tret = write_object_file(buf, size, type, oid);\n+\telse\n+\t\thash_object_file(the_hash_algo, buf, size, type, oid);\n+\n+\tstrbuf_release(&nbuf);\n+\treturn ret;\n+}\n+\n+static int index_stream_convert_blob(struct index_state *istate,\n+\t\t\t\t     struct object_id *oid,\n+\t\t\t\t     int fd,\n+\t\t\t\t     const char *path,\n+\t\t\t\t     unsigned flags)\n+{\n+\tint ret = 0;\n+\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n+\tstruct strbuf sbuf = STRBUF_INIT;\n+\n+\tassert(path);\n+\tassert(would_convert_to_git_filter_fd(istate, path));\n+\n+\tconvert_to_git_filter_fd(istate, path, fd, &sbuf,\n+\t\t\t\t get_conv_flags(flags));\n+\n+\tif (write_object)\n+\t\tret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,\n+\t\t\t\t\toid);\n+\telse\n+\t\thash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,\n+\t\t\t\t oid);\n+\tstrbuf_release(&sbuf);\n+\treturn ret;\n+}\n+\n+static int index_pipe(struct index_state *istate, struct object_id *oid,\n+\t\t      int fd, enum object_type type,\n+\t\t      const char *path, unsigned flags)\n+{\n+\tstruct strbuf sbuf = STRBUF_INIT;\n+\tint ret;\n+\n+\tif (strbuf_read(&sbuf, fd, 4096) >= 0)\n+\t\tret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);\n+\telse\n+\t\tret = -1;\n+\tstrbuf_release(&sbuf);\n+\treturn ret;\n+}\n+\n+#define SMALL_FILE_SIZE (32*1024)\n+\n+static int index_core(struct index_state *istate,\n+\t\t      struct object_id *oid, int fd, size_t size,\n+\t\t      enum object_type type, const char *path,\n+\t\t      unsigned flags)\n+{\n+\tint ret;\n+\n+\tif (!size) {\n+\t\tret = index_mem(istate, oid, \"\", size, type, path, flags);\n+\t} else if (size <= SMALL_FILE_SIZE) {\n+\t\tchar *buf = xmalloc(size);\n+\t\tssize_t read_result = read_in_full(fd, buf, size);\n+\t\tif (read_result < 0)\n+\t\t\tret = error_errno(_(\"read error while indexing %s\"),\n+\t\t\t\t\t  path ? path : \"<unknown>\");\n+\t\telse if (read_result != size)\n+\t\t\tret = error(_(\"short read while indexing %s\"),\n+\t\t\t\t    path ? path : \"<unknown>\");\n+\t\telse\n+\t\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n+\t\tfree(buf);\n+\t} else {\n+\t\tvoid *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n+\t\tret = index_mem(istate, oid, buf, size, type, path, flags);\n+\t\tmunmap(buf, size);\n+\t}\n+\treturn ret;\n+}\n+\n+/*\n+ * This creates one packfile per large blob unless bulk-checkin\n+ * machinery is \"plugged\".\n+ *\n+ * This also bypasses the usual \"convert-to-git\" dance, and that is on\n+ * purpose. We could write a streaming version of the converting\n+ * functions and insert that before feeding the data to fast-import\n+ * (or equivalent in-core API described above). However, that is\n+ * somewhat complicated, as we do not know the size of the filter\n+ * result, which we need to know beforehand when writing a git object.\n+ * Since the primary motivation for trying to stream from the working\n+ * tree file and to avoid mmaping it in core is to deal with large\n+ * binary blobs, they generally do not want to get any conversion, and\n+ * callers should avoid this code path when filters are requested.\n+ */\n+static int index_blob_stream(struct object_id *oid, int fd, size_t size,\n+\t\t\t     const char *path,\n+\t\t\t     unsigned flags)\n+{\n+\treturn index_blob_bulk_checkin(oid, fd, size, path, flags);\n+}\n+\n+int index_fd(struct index_state *istate, struct object_id *oid,\n+\t     int fd, struct stat *st,\n+\t     enum object_type type, const char *path, unsigned flags)\n+{\n+\tint ret;\n+\n+\t/*\n+\t * Call xsize_t() only when needed to avoid potentially unnecessary\n+\t * die() for large files.\n+\t */\n+\tif (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))\n+\t\tret = index_stream_convert_blob(istate, oid, fd, path, flags);\n+\telse if (!S_ISREG(st->st_mode))\n+\t\tret = index_pipe(istate, oid, fd, type, path, flags);\n+\telse if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) ||\n+\t\t type != OBJ_BLOB ||\n+\t\t (path && would_convert_to_git(istate, path)))\n+\t\tret = index_core(istate, oid, fd, xsize_t(st->st_size),\n+\t\t\t\t type, path, flags);\n+\telse\n+\t\tret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,\n+\t\t\t\t\tflags);\n+\tclose(fd);\n+\treturn ret;\n+}\n+\n+int index_path(struct index_state *istate, struct object_id *oid,\n+\t       const char *path, struct stat *st, unsigned flags)\n+{\n+\tint fd;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint rc = 0;\n+\n+\tswitch (st->st_mode & S_IFMT) {\n+\tcase S_IFREG:\n+\t\tfd = open(path, O_RDONLY);\n+\t\tif (fd < 0)\n+\t\t\treturn error_errno(\"open(\\\"%s\\\")\", path);\n+\t\tif (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)\n+\t\t\treturn error(_(\"%s: failed to insert into database\"),\n+\t\t\t\t     path);\n+\t\tbreak;\n+\tcase S_IFLNK:\n+\t\tif (strbuf_readlink(&sb, path, st->st_size))\n+\t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n+\t\tif (!(flags & INDEX_WRITE_OBJECT))\n+\t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n+\t\t\t\t\t OBJ_BLOB, oid);\n+\t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\n+\t\t\trc = error(_(\"%s: failed to insert into database\"), path);\n+\t\tstrbuf_release(&sb);\n+\t\tbreak;\n+\tcase S_IFDIR:\n+\t\treturn repo_resolve_gitlink_ref(the_repository, path, \"HEAD\", oid);\n+\tdefault:\n+\t\treturn error(_(\"%s: unsupported file type\"), path);\n+\t}\n+\treturn rc;\n+}\ndiff --git a/read-cache.h b/read-cache.h\nindex 043da1f1aae..d655eb64bb7 100644\n--- a/read-cache.h\n+++ b/read-cache.h\n@@ -42,4 +42,13 @@ static inline int ce_path_match(struct index_state *istate,\n \t\t\t      S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));\n }\n \n+enum {\n+\tINDEX_WRITE_OBJECT = (1 << 0),\n+\tINDEX_FORMAT_CHECK = (1 << 1),\n+\tINDEX_RENORMALIZE  = (1 << 2),\n+};\n+\n+int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n+int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n+\n #endif /* READ_CACHE_H */\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515987","messageId":"20250411-pks-split-object-file-v2-7-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 7/9] object: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:56Z","receivedAt":"2025-04-11T09:30:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Split out functions relating to the object store subsystem from\n\"object.c\". This helps us to separate concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-store-ll.h |  3 ---\n object-store.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n object.c          | 67 -------------------------------------------------------\n 3 files changed, 66 insertions(+), 70 deletions(-)\n\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8ae80b8a5fa..8bb0f33f9a8 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -92,9 +92,6 @@ struct oidtree *odb_loose_cache(struct object_directory *odb,\n /* Empty the loose object cache for the specified object directory. */\n void odb_clear_loose_cache(struct object_directory *odb);\n \n-/* Clear and free the specified object directory */\n-void free_object_directory(struct object_directory *odb);\n-\n struct packed_git {\n \tstruct hashmap_entry packmap_ent;\n \tstruct packed_git *next;\ndiff --git a/object-store.c b/object-store.c\nindex e5f1f00cdde..ea2d86c429b 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -2,11 +2,13 @@\n \n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n+#include \"commit-graph.h\"\n #include \"config.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n+#include \"loose.h\"\n #include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n@@ -361,6 +363,14 @@ struct object_directory *set_temporary_primary_odb(const char *dir, int will_des\n \treturn new_odb->next;\n }\n \n+static void free_object_directory(struct object_directory *odb)\n+{\n+\tfree(odb->path);\n+\todb_clear_loose_cache(odb);\n+\tloose_object_map_clear(&odb->loose_map);\n+\tfree(odb);\n+}\n+\n void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n {\n \tstruct object_directory *cur_odb = the_repository->objects->odb;\n@@ -970,3 +980,59 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect)\n \t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n \t\t    type_name(expect));\n }\n+\n+struct raw_object_store *raw_object_store_new(void)\n+{\n+\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n+\n+\tmemset(o, 0, sizeof(*o));\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n+\tpthread_mutex_init(&o->replace_mutex, NULL);\n+\treturn o;\n+}\n+\n+static void free_object_directories(struct raw_object_store *o)\n+{\n+\twhile (o->odb) {\n+\t\tstruct object_directory *next;\n+\n+\t\tnext = o->odb->next;\n+\t\tfree_object_directory(o->odb);\n+\t\to->odb = next;\n+\t}\n+\tkh_destroy_odb_path_map(o->odb_by_path);\n+\to->odb_by_path = NULL;\n+}\n+\n+void raw_object_store_clear(struct raw_object_store *o)\n+{\n+\tFREE_AND_NULL(o->alternate_db);\n+\n+\toidmap_free(o->replace_map, 1);\n+\tFREE_AND_NULL(o->replace_map);\n+\tpthread_mutex_destroy(&o->replace_mutex);\n+\n+\tfree_commit_graph(o->commit_graph);\n+\to->commit_graph = NULL;\n+\to->commit_graph_attempted = 0;\n+\n+\tfree_object_directories(o);\n+\to->odb_tail = NULL;\n+\to->loaded_alternates = 0;\n+\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\tclose_object_store(o);\n+\n+\t/*\n+\t * `close_object_store()` only closes the packfiles, but doesn't free\n+\t * them. We thus have to do this manually.\n+\t */\n+\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n+\t\tnext = p->next;\n+\t\tfree(p);\n+\t}\n+\to->packed_git = NULL;\n+\n+\thashmap_clear(&o->pack_map);\n+}\ndiff --git a/object.c b/object.c\nindex 154525a4972..ccda798b75f 100644\n--- a/object.c\n+++ b/object.c\n@@ -6,16 +6,13 @@\n #include \"object.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store.h\"\n #include \"blob.h\"\n #include \"statinfo.h\"\n #include \"tree.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"alloc.h\"\n-#include \"packfile.h\"\n #include \"commit-graph.h\"\n-#include \"loose.h\"\n \n unsigned int get_max_object_index(const struct repository *repo)\n {\n@@ -567,70 +564,6 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo)\n \treturn o;\n }\n \n-struct raw_object_store *raw_object_store_new(void)\n-{\n-\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n-\n-\tmemset(o, 0, sizeof(*o));\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n-\tpthread_mutex_init(&o->replace_mutex, NULL);\n-\treturn o;\n-}\n-\n-void free_object_directory(struct object_directory *odb)\n-{\n-\tfree(odb->path);\n-\todb_clear_loose_cache(odb);\n-\tloose_object_map_clear(&odb->loose_map);\n-\tfree(odb);\n-}\n-\n-static void free_object_directories(struct raw_object_store *o)\n-{\n-\twhile (o->odb) {\n-\t\tstruct object_directory *next;\n-\n-\t\tnext = o->odb->next;\n-\t\tfree_object_directory(o->odb);\n-\t\to->odb = next;\n-\t}\n-\tkh_destroy_odb_path_map(o->odb_by_path);\n-\to->odb_by_path = NULL;\n-}\n-\n-void raw_object_store_clear(struct raw_object_store *o)\n-{\n-\tFREE_AND_NULL(o->alternate_db);\n-\n-\toidmap_free(o->replace_map, 1);\n-\tFREE_AND_NULL(o->replace_map);\n-\tpthread_mutex_destroy(&o->replace_mutex);\n-\n-\tfree_commit_graph(o->commit_graph);\n-\to->commit_graph = NULL;\n-\to->commit_graph_attempted = 0;\n-\n-\tfree_object_directories(o);\n-\to->odb_tail = NULL;\n-\to->loaded_alternates = 0;\n-\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\tclose_object_store(o);\n-\n-\t/*\n-\t * `close_object_store()` only closes the packfiles, but doesn't free\n-\t * them. We thus have to do this manually.\n-\t */\n-\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n-\t\tnext = p->next;\n-\t\tfree(p);\n-\t}\n-\to->packed_git = NULL;\n-\n-\thashmap_clear(&o->pack_map);\n-}\n-\n void parsed_object_pool_reset_commit_grafts(struct parsed_object_pool *o)\n {\n \tfor (int i = 0; i < o->grafts_nr; i++) {\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515988","messageId":"20250411-pks-split-object-file-v2-8-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 8/9] object-store: remove global array of cached objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:57Z","receivedAt":"2025-04-11T09:30:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Cached objects are virtual objects that can be set up without writing\nanything into the object store directly. This mechanism for example\nallows us to create fake commits in git-blame(1).\n\nThe cached objects are stored in a global variable. Refactor the code so\nthat we instead store the array as part of the raw object store. This is\nanother step into the direction of libifying our object database.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n blame.c           |  2 +-\n object-store-ll.h | 14 +++++++++++++-\n object-store.c    | 39 +++++++++++++++++++++++----------------\n 3 files changed, 37 insertions(+), 18 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 703dab43e78..b7c5bd692e6 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -277,7 +277,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,\n \tconvert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);\n \torigin->file.ptr = buf.buf;\n \torigin->file.size = buf.len;\n-\tpretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n+\tpretend_object_file(the_repository, buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n \n \t/*\n \t * Read the current index, replace the path entry with\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8bb0f33f9a8..bb5e8798a1b 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -151,6 +151,8 @@ static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n }\n \n+struct cached_object_entry;\n+\n struct raw_object_store {\n \t/*\n \t * Set of all object directories; the main directory is first (and\n@@ -203,6 +205,15 @@ struct raw_object_store {\n \t\tunsigned flags;\n \t} kept_pack_cache;\n \n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n \t/*\n \t * A map of packfiles to packed_git structs for tracking which\n \t * packs have been loaded already.\n@@ -272,7 +283,8 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n  * object in persistent storage before writing any other new objects\n  * that reference it.\n  */\n-int pretend_object_file(void *, unsigned long, enum object_type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid);\n \n struct object_info {\ndiff --git a/object-store.c b/object-store.c\nindex ea2d86c429b..17fa06a86fa 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -30,31 +30,31 @@\n  * to write them into the object store (e.g. a browse-only\n  * application).\n  */\n-static struct cached_object_entry {\n+struct cached_object_entry {\n \tstruct object_id oid;\n \tstruct cached_object {\n \t\tenum object_type type;\n \t\tconst void *buf;\n \t\tunsigned long size;\n \t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n+};\n \n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n+static const struct cached_object *find_cached_object(struct raw_object_store *object_store,\n+\t\t\t\t\t\t      const struct object_id *oid)\n {\n \tstatic const struct cached_object empty_tree = {\n \t\t.type = OBJ_TREE,\n \t\t.buf = \"\",\n \t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n+\tconst struct cached_object_entry *co = object_store->cached_objects;\n \n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\tfor (size_t i = 0; i < object_store->cached_object_nr; i++, co++)\n \t\tif (oideq(&co->oid, oid))\n \t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n+\n+\tif (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))\n \t\treturn &empty_tree;\n+\n \treturn NULL;\n }\n \n@@ -650,7 +650,7 @@ static int do_oid_object_info_extended(struct repository *r,\n \tif (!oi)\n \t\toi = &blank_oi;\n \n-\tco = find_cached_object(real);\n+\tco = find_cached_object(r->objects, real);\n \tif (co) {\n \t\tif (oi->typep)\n \t\t\t*(oi->typep) = co->type;\n@@ -853,18 +853,21 @@ int oid_object_info(struct repository *r,\n \treturn type;\n }\n \n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid)\n {\n \tstruct cached_object_entry *co;\n \tchar *co_buf;\n \n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n+\thash_object_file(repo->hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(repo, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(repo->objects, oid))\n \t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n+\n+\tALLOC_GROW(repo->objects->cached_objects,\n+\t\t   repo->objects->cached_object_nr + 1, repo->objects->cached_object_alloc);\n+\tco = &repo->objects->cached_objects[repo->objects->cached_object_nr++];\n \tco->value.size = len;\n \tco->value.type = type;\n \tco_buf = xmalloc(len);\n@@ -1021,6 +1024,10 @@ void raw_object_store_clear(struct raw_object_store *o)\n \to->odb_tail = NULL;\n \to->loaded_alternates = 0;\n \n+\tfor (size_t i = 0; i < o->cached_object_nr; i++)\n+\t\tfree((char *) o->cached_objects[i].value.buf);\n+\tFREE_AND_NULL(o->cached_objects);\n+\n \tINIT_LIST_HEAD(&o->packed_git_mru);\n \tclose_object_store(o);\n \n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"515989","messageId":"20250411-pks-split-object-file-v2-9-2bea0c9033ae@pks.im","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im","subject":"[PATCH v2 9/9] object-store: merge \"object-store-ll.h\" and \"object-store.h\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-11T09:29:58Z","receivedAt":"2025-04-11T09:30:10Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"object-store-ll.h\" header has been introduced to keep transitive\nheader dependendcies and compile times at bay. Now that we have created\na new \"object-store.c\" file though we can easily move the last remaining\nadditional bit of \"object-store.h\", the `odb_path_map`, out of the\nheader.\n\nDo so. As the \"object-store.h\" header is now equivalent to its low-level\nalternative we drop the latter and inline it into the former.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n apply.c                       |   2 +-\n archive-tar.c                 |   2 +-\n archive-zip.c                 |   2 +-\n archive.c                     |   2 +-\n attr.c                        |   2 +-\n bisect.c                      |   2 +-\n blame.c                       |   2 +-\n builtin/backfill.c            |   2 +-\n builtin/blame.c               |   2 +-\n builtin/cat-file.c            |   2 +-\n builtin/checkout.c            |   2 +-\n builtin/clone.c               |   2 +-\n builtin/commit-graph.c        |   2 +-\n builtin/commit-tree.c         |   2 +-\n builtin/count-objects.c       |   2 +-\n builtin/describe.c            |   2 +-\n builtin/difftool.c            |   2 +-\n builtin/fast-export.c         |   2 +-\n builtin/fast-import.c         |   2 +-\n builtin/fetch.c               |   2 +-\n builtin/fsck.c                |   2 +-\n builtin/gc.c                  |   2 +-\n builtin/grep.c                |   2 +-\n builtin/hash-object.c         |   2 +-\n builtin/index-pack.c          |   2 +-\n builtin/log.c                 |   2 +-\n builtin/ls-tree.c             |   2 +-\n builtin/merge-tree.c          |   2 +-\n builtin/mktag.c               |   2 +-\n builtin/mktree.c              |   2 +-\n builtin/multi-pack-index.c    |   2 +-\n builtin/notes.c               |   2 +-\n builtin/pack-objects.c        |   2 +-\n builtin/pack-redundant.c      |   2 +-\n builtin/prune.c               |   2 +-\n builtin/receive-pack.c        |   2 +-\n builtin/remote.c              |   2 +-\n builtin/repack.c              |   2 +-\n builtin/replace.c             |   2 +-\n builtin/rev-list.c            |   2 +-\n builtin/show-ref.c            |   2 +-\n builtin/submodule--helper.c   |   2 +-\n builtin/tag.c                 |   2 +-\n builtin/unpack-file.c         |   2 +-\n builtin/unpack-objects.c      |   2 +-\n bulk-checkin.c                |   2 +-\n bundle-uri.c                  |   2 +-\n bundle.c                      |   2 +-\n cache-tree.c                  |   2 +-\n combine-diff.c                |   2 +-\n commit-graph.c                |   2 +-\n commit-graph.h                |   2 +-\n commit.c                      |   2 +-\n config.c                      |   2 +-\n connected.c                   |   2 +-\n convert.c                     |   2 +-\n diagnose.c                    |   2 +-\n diff.c                        |   2 +-\n diffcore-rename.c             |   2 +-\n dir.c                         |   2 +-\n entry.c                       |   2 +-\n fetch-pack.c                  |   2 +-\n fmt-merge-msg.c               |   2 +-\n fsck.c                        |   2 +-\n grep.c                        |   2 +-\n http-backend.c                |   2 +-\n http-push.c                   |   2 +-\n http-walker.c                 |   2 +-\n http.c                        |   2 +-\n list-objects-filter.c         |   2 +-\n list-objects.c                |   2 +-\n log-tree.c                    |   2 +-\n mailmap.c                     |   2 +-\n merge-blobs.c                 |   2 +-\n merge-recursive.c             |   2 +-\n notes-merge.c                 |   2 +-\n object-file.c                 |   1 +\n object-name.c                 |   2 +-\n object-store-ll.h             | 517 ------------------------------------------\n object-store.c                |   5 +\n object-store.h                | 516 ++++++++++++++++++++++++++++++++++++++++-\n oss-fuzz/fuzz-pack-idx.c      |   2 +-\n pack-bitmap-write.c           |   2 +-\n pack-bitmap.c                 |   2 +-\n pack-check.c                  |   2 +-\n pack-mtimes.c                 |   2 +-\n pack-objects.h                |   2 +-\n pack-revindex.c               |   2 +-\n packfile.c                    |   2 +-\n path.c                        |   2 +-\n promisor-remote.c             |   2 +-\n protocol-caps.c               |   2 +-\n prune-packed.c                |   2 +-\n reachable.c                   |   2 +-\n read-cache.c                  |   2 +-\n ref-filter.c                  |   2 +-\n reflog.c                      |   2 +-\n refs.c                        |   2 +-\n remote.c                      |   2 +-\n replace-object.c              |   2 +-\n replace-object.h              |   2 +-\n repository.c                  |   2 +-\n rerere.c                      |   2 +-\n revision.c                    |   2 +-\n send-pack.c                   |   2 +-\n sequencer.c                   |   2 +-\n server-info.c                 |   2 +-\n shallow.c                     |   2 +-\n streaming.c                   |   2 +-\n submodule-config.c            |   2 +-\n submodule.c                   |   2 +-\n t/helper/test-pack-mtimes.c   |   2 +-\n t/helper/test-partial-clone.c |   2 +-\n t/helper/test-read-graph.c    |   2 +-\n t/helper/test-read-midx.c     |   2 +-\n t/helper/test-ref-store.c     |   2 +-\n tag.c                         |   2 +-\n tmp-objdir.c                  |   2 +-\n tree-walk.c                   |   2 +-\n tree.c                        |   2 +-\n unpack-trees.c                |   2 +-\n upload-pack.c                 |   2 +-\n walker.c                      |   2 +-\n xdiff-interface.c             |   2 +-\n 124 files changed, 637 insertions(+), 642 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex f274a379487..2b6f4d0af87 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -14,7 +14,7 @@\n #include \"abspath.h\"\n #include \"base85.h\"\n #include \"config.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"delta.h\"\n #include \"diff.h\"\n #include \"dir.h\"\ndiff --git a/archive-tar.c b/archive-tar.c\nindex 0edf13fba75..282b48196f9 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -11,7 +11,7 @@\n #include \"hex.h\"\n #include \"tar.h\"\n #include \"archive.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"streaming.h\"\n #include \"run-command.h\"\ndiff --git a/archive-zip.c b/archive-zip.c\nindex 9f32730181b..405da6f3d83 100644\n--- a/archive-zip.c\n+++ b/archive-zip.c\n@@ -12,7 +12,7 @@\n #include \"hex.h\"\n #include \"streaming.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"userdiff.h\"\n #include \"write-or-die.h\"\ndiff --git a/archive.c b/archive.c\nindex c95e3981524..014c312178c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -14,7 +14,7 @@\n #include \"pretty.h\"\n #include \"setup.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\ndiff --git a/attr.c b/attr.c\nindex 0bd2750528f..86b6109fc4e 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -22,7 +22,7 @@\n #include \"read-cache-ll.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/bisect.c b/bisect.c\nindex 269a98bf978..a327468c75b 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -20,7 +20,7 @@\n #include \"commit-slab.h\"\n #include \"commit-reach.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"dir.h\"\n \ndiff --git a/blame.c b/blame.c\nindex b7c5bd692e6..57daa45e899 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"cache-tree.h\"\n #include \"mergesort.h\"\n #include \"commit.h\"\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex 33e1ea2f84f..aaa104bc91d 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -13,7 +13,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"promisor-remote.h\"\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c470654c7ec..4e156bfd19d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -28,7 +28,7 @@\n #include \"line-log.h\"\n #include \"progress.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"blame.h\"\n #include \"refs.h\"\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex b13561cf73b..c870fde260a 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -22,7 +22,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"promisor-remote.h\"\n #include \"mailmap.h\"\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3e68623838a..8136962e2b3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -20,7 +20,7 @@\n #include \"merge-recursive.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"preload-index.h\"\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 2993acb630e..d0423a2198b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -25,7 +25,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\ndiff --git a/builtin/commit-graph.c b/builtin/commit-graph.c\nindex 8ca75262c59..be06d0a811b 100644\n--- a/builtin/commit-graph.c\n+++ b/builtin/commit-graph.c\n@@ -6,7 +6,7 @@\n #include \"hex.h\"\n #include \"parse-options.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"progress.h\"\n #include \"replace-object.h\"\n #include \"strbuf.h\"\ndiff --git a/builtin/commit-tree.c b/builtin/commit-tree.c\nindex 38457600a4e..6f9975e7a88 100644\n--- a/builtin/commit-tree.c\n+++ b/builtin/commit-tree.c\n@@ -9,7 +9,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n #include \"commit.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 1e89148ed74..0bb5360b2f2 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -12,7 +12,7 @@\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static unsigned long garbage;\n static off_t size_garbage;\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 23df333fd04..0f87fbceef3 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -19,7 +19,7 @@\n #include \"setup.h\"\n #include \"strvec.h\"\n #include \"run-command.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects.h\"\n #include \"commit-slab.h\"\n #include \"wildmatch.h\"\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 45d6ea8a801..e6e45436531 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -29,7 +29,7 @@\n #include \"strbuf.h\"\n #include \"lockfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"entry.h\"\n #include \"setup.h\"\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 170126d41ac..afacd228b5d 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 63880b595cc..0357211ea6c 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -24,7 +24,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"mem-pool.h\"\n #include \"commit-reach.h\"\n #include \"khash.h\"\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 02af5054690..b52a32a5e0d 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"oid-array.h\"\n #include \"commit.h\"\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 32d40d8f9fc..47055ebaf9a 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -18,7 +18,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex b069629676c..def922e7145 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -29,7 +29,7 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\n #include \"path.h\"\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 283d64cab80..bcfbe5be5ba 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -26,7 +26,7 @@\n #include \"submodule-config.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pager.h\"\n #include \"path.h\"\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 7aa889b1b4a..e4eddd63bf4 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -11,7 +11,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"blob.h\"\n #include \"quote.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex de127c0ff13..60a8ee05dbc 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -21,7 +21,7 @@\n #include \"packfile.h\"\n #include \"pack-revindex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"path.h\"\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 06ffaa93e86..72ac45e9433 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -16,7 +16,7 @@\n #include \"hex.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"color.h\"\n #include \"commit.h\"\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8542b5d53e4..8aafc30ca48 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -10,7 +10,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"path.h\"\n #include \"quote.h\"\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 3ec7127b3a6..4aafa73c615 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -10,7 +10,7 @@\n #include \"commit-reach.h\"\n #include \"merge-ort.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 6e188dce50a..7ac11c46d53 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -6,7 +6,7 @@\n #include \"strbuf.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"fsck.h\"\n #include \"config.h\"\n \ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 0644f951161..7ffe6eefd8a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -12,7 +12,7 @@\n #include \"tree.h\"\n #include \"parse-options.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct treeent {\n \tunsigned mode;\ndiff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\nindex 2a938466f53..d98410ca6c6 100644\n--- a/builtin/multi-pack-index.c\n+++ b/builtin/multi-pack-index.c\n@@ -7,7 +7,7 @@\n #include \"midx.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 0dbc233752d..a3f433ca4c0 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -16,7 +16,7 @@\n #include \"notes.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n \n #include \"pretty.h\"\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 163aab547fe..488c80f2cf3 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -32,7 +32,7 @@\n #include \"list.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"dir.h\"\n #include \"midx.h\"\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex 3febe732f8e..5d1fc781761 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -13,7 +13,7 @@\n #include \"hex.h\"\n \n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n \n #define BLKSIZE 512\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex 8f52da8bd66..e930caa0c0a 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -17,7 +17,7 @@\n #include \"replace-object.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"shallow.h\"\n \n static const char * const prune_usage[] = {\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex ee51bd76f60..be314879e82 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -33,7 +33,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"protocol.h\"\n #include \"commit-reach.h\"\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 1b7aad88380..59481b3a82c 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"rebase.h\"\n #include \"refs.h\"\n #include \"refspec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strvec.h\"\n #include \"commit-reach.h\"\n #include \"progress.h\"\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f3330ade7b8..1fd2874324a 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -17,7 +17,7 @@\n #include \"midx.h\"\n #include \"packfile.h\"\n #include \"prune-packed.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"shallow.h\"\n #include \"pack.h\"\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex ce2948a9b2a..4204ca3605b 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache.h\"\n #include \"replace-object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex bb26bee0d45..0170d79b62f 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -14,7 +14,7 @@\n #include \"object.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"log-tree.h\"\n #include \"graph.h\"\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 285cd3e4338..f81209f23c3 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"refs/refs-internal.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 570226ea166..a8d1200b7cb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -28,7 +28,7 @@\n #include \"diff.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"advice.h\"\n #include \"branch.h\"\n #include \"list-objects-filter-options.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 536a01ff3ae..e6b372cebf5 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -19,7 +19,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"tag.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex b19e5cabd03..e33acfc4ee4 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static char *create_temp_file(struct object_id *oid)\n {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 4078eab9252..661be789f13 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -9,7 +9,7 @@\n #include \"git-zlib.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"delta.h\"\n #include \"pack.h\"\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 0b9bad92868..b38937d739b 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -19,7 +19,7 @@\n #include \"tmp-objdir.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int odb_transaction_nesting;\n \ndiff --git a/bundle-uri.c b/bundle-uri.c\nindex 744257c49c1..96d2ba726d9 100644\n--- a/bundle-uri.c\n+++ b/bundle-uri.c\n@@ -14,7 +14,7 @@\n #include \"fetch-pack.h\"\n #include \"remote.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct {\n \tenum bundle_list_heuristic heuristic;\ndiff --git a/bundle.c b/bundle.c\nindex d7ad6908433..d661c4ec214 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -7,7 +7,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 4c8167ea927..c0e1e9ee1d4 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -10,7 +10,7 @@\n #include \"cache-tree.h\"\n #include \"bulk-checkin.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 553bf59fed6..dfae9f7995d 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -2,7 +2,7 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"convert.h\"\n #include \"diff.h\"\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 8060c358b84..a36c2f1e303 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"alloc.h\"\ndiff --git a/commit-graph.h b/commit-graph.h\nindex 67819401954..13f662827d4 100644\n--- a/commit-graph.h\n+++ b/commit-graph.h\n@@ -1,7 +1,7 @@\n #ifndef COMMIT_GRAPH_H\n #define COMMIT_GRAPH_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n \n #define GIT_TEST_COMMIT_GRAPH \"GIT_TEST_COMMIT_GRAPH\"\ndiff --git a/commit.c b/commit.c\nindex fbf4f8e87fd..00842678bd2 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -9,7 +9,7 @@\n #include \"hex.h\"\n #include \"repository.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/config.c b/config.c\nindex accb47e2d18..b18b5617fcd 100644\n--- a/config.c\n+++ b/config.c\n@@ -31,7 +31,7 @@\n #include \"hashmap.h\"\n #include \"string-list.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"path.h\"\n #include \"utf8.h\"\ndiff --git a/connected.c b/connected.c\nindex 3099da84f33..4415388beba 100644\n--- a/connected.c\n+++ b/connected.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n #include \"connected.h\"\ndiff --git a/convert.c b/convert.c\nindex 9cc0ca20ca0..8783e17941f 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -8,7 +8,7 @@\n #include \"copy.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"attr.h\"\n #include \"run-command.h\"\n #include \"quote.h\"\ndiff --git a/diagnose.c b/diagnose.c\nindex bd485effea2..b1be74be983 100644\n--- a/diagnose.c\n+++ b/diagnose.c\n@@ -7,7 +7,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"strvec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"parse-options.h\"\n #include \"repository.h\"\ndiff --git a/diff.c b/diff.c\nindex c361199ab1b..9f2042fd1a5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -23,7 +23,7 @@\n #include \"color.h\"\n #include \"run-command.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"userdiff.h\"\n #include \"submodule.h\"\n #include \"hashmap.h\"\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 5002e896aad..787a2cef5f4 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -8,7 +8,7 @@\n #include \"git-compat-util.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"hashmap.h\"\n #include \"mem-pool.h\"\n #include \"oid-array.h\"\ndiff --git a/dir.c b/dir.c\nindex 16ae3b5169d..1f8b6e2aa67 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/entry.c b/entry.c\nindex 81b321e53d1..f36ec5ad242 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -1,7 +1,7 @@\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 1ed5e11dd56..210dc30d50f 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -24,7 +24,7 @@\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"connected.h\"\n #include \"fetch-negotiator.h\"\ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex 5b63c3b088a..501b5acdd44 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -6,7 +6,7 @@\n #include \"environment.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"diff.h\"\n #include \"diff-merges.h\"\n #include \"hex.h\"\ndiff --git a/fsck.c b/fsck.c\nindex 9fc4c25ffd5..8dc8472ceb3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"dir.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"object.h\"\ndiff --git a/grep.c b/grep.c\nindex 9284b5741f5..f8d535182c3 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"grep.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n #include \"xdiff-interface.h\"\ndiff --git a/http-backend.c b/http-backend.c\nindex 50b2858fad6..0c575aa88aa 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -18,7 +18,7 @@\n #include \"url.h\"\n #include \"strvec.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"protocol.h\"\n #include \"date.h\"\n #include \"write-or-die.h\"\ndiff --git a/http-push.c b/http-push.c\nindex 806eb67cf1b..32e37565f4e 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -20,7 +20,7 @@\n #include \"url.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n \n #ifdef EXPAT_NEEDS_XMLPARSE_H\ndiff --git a/http-walker.c b/http-walker.c\nindex 7918ddc0968..882cae19c24 100644\n--- a/http-walker.c\n+++ b/http-walker.c\n@@ -9,7 +9,7 @@\n #include \"list.h\"\n #include \"transport.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct alt_base {\n \tchar *base;\ndiff --git a/http.c b/http.c\nindex 0c9a872809f..bc18ff83c4b 100644\n--- a/http.c\n+++ b/http.c\n@@ -19,7 +19,7 @@\n #include \"packfile.h\"\n #include \"string-list.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tempfile.h\"\n \n static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);\ndiff --git a/list-objects-filter.c b/list-objects-filter.c\nindex dc598a081bb..7765761b3c6 100644\n--- a/list-objects-filter.c\n+++ b/list-objects-filter.c\n@@ -12,7 +12,7 @@\n #include \"oidmap.h\"\n #include \"oidset.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n /* Remember to update object flag allocation in object.h */\n /*\ndiff --git a/list-objects.c b/list-objects.c\nindex 943e62e868f..1e5512e1318 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -14,7 +14,7 @@\n #include \"list-objects-filter.h\"\n #include \"list-objects-filter-options.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace.h\"\n #include \"environment.h\"\n \ndiff --git a/log-tree.c b/log-tree.c\nindex 5dd1b63076f..a4d4ab59ca0 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -9,7 +9,7 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"tmp-objdir.h\"\n #include \"commit.h\"\ndiff --git a/mailmap.c b/mailmap.c\nindex f35d20ed7fd..9e2642a043b 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -6,7 +6,7 @@\n #include \"string-list.h\"\n #include \"mailmap.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n char *git_mailmap_file;\ndiff --git a/merge-blobs.c b/merge-blobs.c\nindex 0ad0390fea5..53f36dbc175 100644\n--- a/merge-blobs.c\n+++ b/merge-blobs.c\n@@ -4,7 +4,7 @@\n #include \"merge-ll.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)\n {\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9aedffc546b..981e57698ed 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -27,7 +27,7 @@\n #include \"name-hash.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"revision.h\"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 13750490eff..96e59322a8b 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -8,7 +8,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"read-cache.h\"\n #include \"repository.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 235d29858ff..4abca6cb613 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -12,6 +12,7 @@\n \n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\ndiff --git a/object-name.c b/object-name.c\nindex 91f731373a1..2c751a5352a 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -19,7 +19,7 @@\n #include \"oidtree.h\"\n #include \"packfile.h\"\n #include \"pretty.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/object-store-ll.h b/object-store-ll.h\ndeleted file mode 100644\nindex bb5e8798a1b..00000000000\n--- a/object-store-ll.h\n+++ /dev/null\n@@ -1,517 +0,0 @@\n-#ifndef OBJECT_STORE_LL_H\n-#define OBJECT_STORE_LL_H\n-\n-#include \"hashmap.h\"\n-#include \"object.h\"\n-#include \"list.h\"\n-#include \"thread-utils.h\"\n-#include \"oidset.h\"\n-\n-struct oidmap;\n-struct oidtree;\n-struct strbuf;\n-struct repository;\n-\n-struct object_directory {\n-\tstruct object_directory *next;\n-\n-\t/*\n-\t * Used to store the results of readdir(3) calls when we are OK\n-\t * sacrificing accuracy due to races for speed. That includes\n-\t * object existence with OBJECT_INFO_QUICK, as well as\n-\t * our search for unique abbreviated hashes. Don't use it for tasks\n-\t * requiring greater accuracy!\n-\t *\n-\t * Be sure to call odb_load_loose_cache() before using.\n-\t */\n-\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n-\tstruct oidtree *loose_objects_cache;\n-\n-\t/* Map between object IDs for loose objects. */\n-\tstruct loose_object_map *loose_map;\n-\n-\t/*\n-\t * This is a temporary object store created by the tmp_objdir\n-\t * facility. Disable ref updates since the objects in the store\n-\t * might be discarded on rollback.\n-\t */\n-\tint disable_ref_updates;\n-\n-\t/*\n-\t * This object store is ephemeral, so there is no need to fsync.\n-\t */\n-\tint will_destroy;\n-\n-\t/*\n-\t * Path to the alternative object store. If this is a relative path,\n-\t * it is relative to the current working directory.\n-\t */\n-\tchar *path;\n-};\n-\n-void prepare_alt_odb(struct repository *r);\n-int has_alt_odb(struct repository *r);\n-char *compute_alternate_path(const char *path, struct strbuf *err);\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n-typedef int alt_odb_fn(struct object_directory *, void *);\n-int foreach_alt_odb(alt_odb_fn, void*);\n-typedef void alternate_ref_fn(const struct object_id *oid, void *);\n-void for_each_alternate_ref(alternate_ref_fn, void *);\n-\n-/*\n- * Add the directory to the on-disk alternates file; the new entry will also\n- * take effect in the current process.\n- */\n-void add_to_alternates_file(const char *dir);\n-\n-/*\n- * Add the directory to the in-memory list of alternates (along with any\n- * recursive alternates it points to), but do not modify the on-disk alternates\n- * file.\n- */\n-void add_to_alternates_memory(const char *dir);\n-\n-/*\n- * Replace the current writable object directory with the specified temporary\n- * object directory; returns the former primary object directory.\n- */\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n-\n-/*\n- * Restore a previous ODB replaced by set_temporary_main_odb.\n- */\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n-\n-/*\n- * Populate and return the loose object cache array corresponding to the\n- * given object ID.\n- */\n-struct oidtree *odb_loose_cache(struct object_directory *odb,\n-\t\t\t\t  const struct object_id *oid);\n-\n-/* Empty the loose object cache for the specified object directory. */\n-void odb_clear_loose_cache(struct object_directory *odb);\n-\n-struct packed_git {\n-\tstruct hashmap_entry packmap_ent;\n-\tstruct packed_git *next;\n-\tstruct list_head mru;\n-\tstruct pack_window *windows;\n-\toff_t pack_size;\n-\tconst void *index_data;\n-\tsize_t index_size;\n-\tuint32_t num_objects;\n-\tsize_t crc_offset;\n-\tstruct oidset bad_objects;\n-\tint index_version;\n-\ttime_t mtime;\n-\tint pack_fd;\n-\tint index;              /* for builtin/pack-objects.c */\n-\tunsigned pack_local:1,\n-\t\t pack_keep:1,\n-\t\t pack_keep_in_core:1,\n-\t\t freshened:1,\n-\t\t do_not_close:1,\n-\t\t pack_promisor:1,\n-\t\t multi_pack_index:1,\n-\t\t is_cruft:1;\n-\tunsigned char hash[GIT_MAX_RAWSZ];\n-\tstruct revindex_entry *revindex;\n-\tconst uint32_t *revindex_data;\n-\tconst uint32_t *revindex_map;\n-\tsize_t revindex_size;\n-\t/*\n-\t * mtimes_map points at the beginning of the memory mapped region of\n-\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n-\t * of that .mtimes file\n-\t */\n-\tconst uint32_t *mtimes_map;\n-\tsize_t mtimes_size;\n-\n-\t/* repo denotes the repository this packfile belongs to */\n-\tstruct repository *repo;\n-\n-\t/* something like \".git/objects/pack/xxxxx.pack\" */\n-\tchar pack_name[FLEX_ARRAY]; /* more */\n-};\n-\n-struct multi_pack_index;\n-\n-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n-\t\t\t\t     const struct hashmap_entry *entry,\n-\t\t\t\t     const struct hashmap_entry *entry2,\n-\t\t\t\t     const void *keydata)\n-{\n-\tconst char *key = keydata;\n-\tconst struct packed_git *pg1, *pg2;\n-\n-\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n-\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n-\n-\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n-}\n-\n-struct cached_object_entry;\n-\n-struct raw_object_store {\n-\t/*\n-\t * Set of all object directories; the main directory is first (and\n-\t * cannot be NULL after initialization). Subsequent directories are\n-\t * alternates.\n-\t */\n-\tstruct object_directory *odb;\n-\tstruct object_directory **odb_tail;\n-\tstruct kh_odb_path_map *odb_by_path;\n-\n-\tint loaded_alternates;\n-\n-\t/*\n-\t * A list of alternate object directories loaded from the environment;\n-\t * this should not generally need to be accessed directly, but will\n-\t * populate the \"odb\" list when prepare_alt_odb() is run.\n-\t */\n-\tchar *alternate_db;\n-\n-\t/*\n-\t * Objects that should be substituted by other objects\n-\t * (see git-replace(1)).\n-\t */\n-\tstruct oidmap *replace_map;\n-\tunsigned replace_map_initialized : 1;\n-\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n-\n-\tstruct commit_graph *commit_graph;\n-\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c and midx.c\n-\t */\n-\tstruct multi_pack_index *multi_pack_index;\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c\n-\t */\n-\n-\tstruct packed_git *packed_git;\n-\t/* A most-recently-used ordered version of the packed_git list. */\n-\tstruct list_head packed_git_mru;\n-\n-\tstruct {\n-\t\tstruct packed_git **packs;\n-\t\tunsigned flags;\n-\t} kept_pack_cache;\n-\n-\t/*\n-\t * This is meant to hold a *small* number of objects that you would\n-\t * want repo_read_object_file() to be able to return, but yet you do not want\n-\t * to write them into the object store (e.g. a browse-only\n-\t * application).\n-\t */\n-\tstruct cached_object_entry *cached_objects;\n-\tsize_t cached_object_nr, cached_object_alloc;\n-\n-\t/*\n-\t * A map of packfiles to packed_git structs for tracking which\n-\t * packs have been loaded already.\n-\t */\n-\tstruct hashmap pack_map;\n-\n-\t/*\n-\t * A fast, rough count of the number of objects in the repository.\n-\t * These two fields are not meant for direct access. Use\n-\t * repo_approximate_object_count() instead.\n-\t */\n-\tunsigned long approximate_object_count;\n-\tunsigned approximate_object_count_valid : 1;\n-\n-\t/*\n-\t * Whether packed_git has already been populated with this repository's\n-\t * packs.\n-\t */\n-\tunsigned packed_git_initialized : 1;\n-};\n-\n-struct raw_object_store *raw_object_store_new(void);\n-void raw_object_store_clear(struct raw_object_store *o);\n-\n-/*\n- * Create a temporary file rooted in the object database directory, or\n- * die on failure. The filename is taken from \"pattern\", which should have the\n- * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n- * \"template\" buffer. Returns the open descriptor.\n- */\n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n-\n-/*\n- * Create a pack .keep file named \"name\" (which should generally be the output\n- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n- * error.\n- */\n-int odb_pack_keep(const char *name);\n-\n-/*\n- * Put in `buf` the name of the file in the local object database that\n- * would be used to store a loose object with the specified oid.\n- */\n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid);\n-\n-void *map_loose_object(struct repository *r, const struct object_id *oid,\n-\t\t       unsigned long *size);\n-\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size);\n-\n-/* Read and unpack an object file into memory, write memory to an object file */\n-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n-\n-void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n-\t\t      unsigned long len, enum object_type type,\n-\t\t      struct object_id *oid);\n-\n-/*\n- * Add an object file to the in-memory object store, without writing it\n- * to disk.\n- *\n- * Callers are responsible for calling write_object_file to record the\n- * object in persistent storage before writing any other new objects\n- * that reference it.\n- */\n-int pretend_object_file(struct repository *repo,\n-\t\t\tvoid *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid);\n-\n-struct object_info {\n-\t/* Request */\n-\tenum object_type *typep;\n-\tunsigned long *sizep;\n-\toff_t *disk_sizep;\n-\tstruct object_id *delta_base_oid;\n-\tstruct strbuf *type_name;\n-\tvoid **contentp;\n-\n-\t/* Response */\n-\tenum {\n-\t\tOI_CACHED,\n-\t\tOI_LOOSE,\n-\t\tOI_PACKED,\n-\t\tOI_DBCACHED\n-\t} whence;\n-\tunion {\n-\t\t/*\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } cached;\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } loose;\n-\t\t */\n-\t\tstruct {\n-\t\t\tstruct packed_git *pack;\n-\t\t\toff_t offset;\n-\t\t\tunsigned int is_delta;\n-\t\t} packed;\n-\t} u;\n-};\n-\n-/*\n- * Initializer for a \"struct object_info\" that wants no items. You may\n- * also memset() the memory to all-zeroes.\n- */\n-#define OBJECT_INFO_INIT { 0 }\n-\n-/* Invoke lookup_replace_object() on the given hash */\n-#define OBJECT_INFO_LOOKUP_REPLACE 1\n-/* Allow reading from a loose object file of unknown/bogus type */\n-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n-/* Do not retry packed storage after checking packed and loose storage */\n-#define OBJECT_INFO_QUICK 8\n-/*\n- * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n- * nonzero).\n- */\n-#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n-/*\n- * This is meant for bulk prefetching of missing blobs in a partial\n- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n- */\n-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n-\n-/* Die if object corruption (not just an object being missing) was detected. */\n-#define OBJECT_INFO_DIE_IF_CORRUPT 32\n-\n-int oid_object_info_extended(struct repository *r,\n-\t\t\t     const struct object_id *,\n-\t\t\t     struct object_info *, unsigned flags);\n-\n-/* Retry packed storage after checking packed and loose storage */\n-#define HAS_OBJECT_RECHECK_PACKED 1\n-\n-/*\n- * Returns 1 if the object exists. This function will not lazily fetch objects\n- * in a partial clone.\n- */\n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags);\n-\n-/*\n- * These macros and functions are deprecated. If checking existence for an\n- * object that is likely to be missing and/or whose absence is relatively\n- * inconsequential (or is consequential but the caller is prepared to handle\n- * it), use has_object(), which has better defaults (no lazy fetch in a partial\n- * clone and no rechecking of packed storage). In the unlikely event that a\n- * caller needs to assert existence of an object that it fully expects to\n- * exist, and wants to trigger a lazy fetch in a partial clone, use\n- * oid_object_info_extended() with a NULL struct object_info.\n- *\n- * These functions can be removed once all callers have migrated to\n- * has_object() and/or oid_object_info_extended().\n- */\n-int repo_has_object_file(struct repository *r, const struct object_id *oid);\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags);\n-\n-void assert_oid_type(const struct object_id *oid, enum object_type expect);\n-\n-/*\n- * Enabling the object read lock allows multiple threads to safely call the\n- * following functions in parallel: repo_read_object_file(),\n- * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n- *\n- * obj_read_lock() and obj_read_unlock() may also be used to protect other\n- * section which cannot execute in parallel with object reading. Since the used\n- * lock is a recursive mutex, these sections can even contain calls to object\n- * reading functions. However, beware that in these cases zlib inflation won't\n- * be performed in parallel, losing performance.\n- *\n- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n- * any of its callees end up calling it, this recursive call won't benefit from\n- * parallel inflation.\n- */\n-void enable_obj_read_lock(void);\n-void disable_obj_read_lock(void);\n-\n-extern int obj_read_use_lock;\n-extern pthread_mutex_t obj_read_mutex;\n-\n-static inline void obj_read_lock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_lock(&obj_read_mutex);\n-}\n-\n-static inline void obj_read_unlock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_unlock(&obj_read_mutex);\n-}\n-\n-/*\n- * Iterate over the files in the loose-object parts of the object\n- * directory \"path\", triggering the following callbacks:\n- *\n- *  - loose_object is called for each loose object we find.\n- *\n- *  - loose_cruft is called for any files that do not appear to be\n- *    loose objects. Note that we only look in the loose object\n- *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n- *    \"objects/foobar\" as cruft.\n- *\n- *  - loose_subdir is called for each top-level hashed subdirectory\n- *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n- *    after the objects in the directory are processed.\n- *\n- * Any callback that is NULL will be ignored. Callbacks returning non-zero\n- * will end the iteration.\n- *\n- * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n- * scratch buffer, but restored to its original contents before\n- * the function returns.\n- */\n-typedef int each_loose_object_fn(const struct object_id *oid,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-typedef int each_loose_cruft_fn(const char *basename,\n-\t\t\t\tconst char *path,\n-\t\t\t\tvoid *data);\n-typedef int each_loose_subdir_fn(unsigned int nr,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n-\t\t\t\tstruct strbuf *path,\n-\t\t\t\teach_loose_object_fn obj_cb,\n-\t\t\t\teach_loose_cruft_fn cruft_cb,\n-\t\t\t\teach_loose_subdir_fn subdir_cb,\n-\t\t\t\tvoid *data);\n-int for_each_loose_file_in_objdir(const char *path,\n-\t\t\t\t  each_loose_object_fn obj_cb,\n-\t\t\t\t  each_loose_cruft_fn cruft_cb,\n-\t\t\t\t  each_loose_subdir_fn subdir_cb,\n-\t\t\t\t  void *data);\n-int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n-\t\t\t\t      each_loose_object_fn obj_cb,\n-\t\t\t\t      each_loose_cruft_fn cruft_cb,\n-\t\t\t\t      each_loose_subdir_fn subdir_cb,\n-\t\t\t\t      void *data);\n-\n-/* Flags for for_each_*_object() below. */\n-enum for_each_object_flags {\n-\t/* Iterate only over local objects, not alternates. */\n-\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n-\n-\t/* Only iterate over packs obtained from the promisor remote. */\n-\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n-\n-\t/*\n-\t * Visit objects within a pack in packfile order rather than .idx order\n-\t */\n-\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n-\n-\t/* Only iterate over packs that are not marked as kept in-core. */\n-\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n-\n-\t/* Only iterate over packs that do not have .keep files. */\n-\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n-};\n-\n-/*\n- * Iterate over all accessible loose objects without respect to\n- * reachability. By default, this includes both local and alternate objects.\n- * The order in which objects are visited is unspecified.\n- *\n- * Any flags specific to packs are ignored.\n- */\n-int for_each_loose_object(each_loose_object_fn, void *,\n-\t\t\t  enum for_each_object_flags flags);\n-\n-/*\n- * Iterate over all accessible packed objects without respect to reachability.\n- * By default, this includes both local and alternate packs.\n- *\n- * Note that some objects may appear twice if they are found in multiple packs.\n- * Each pack is visited in an unspecified order. By default, objects within a\n- * pack are visited in pack-idx order (i.e., sorted by oid).\n- */\n-typedef int each_packed_object_fn(const struct object_id *oid,\n-\t\t\t\t  struct packed_git *pack,\n-\t\t\t\t  uint32_t pos,\n-\t\t\t\t  void *data);\n-int for_each_object_in_pack(struct packed_git *p,\n-\t\t\t    each_packed_object_fn, void *data,\n-\t\t\t    enum for_each_object_flags flags);\n-int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n-\t\t\t   void *data, enum for_each_object_flags flags);\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n-\n-#endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nindex 17fa06a86fa..fc48925d230 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -4,9 +4,11 @@\n #include \"abspath.h\"\n #include \"commit-graph.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n+#include \"khash.h\"\n #include \"lockfile.h\"\n #include \"loose.h\"\n #include \"object-file-convert.h\"\n@@ -24,6 +26,9 @@\n #include \"submodule.h\"\n #include \"write-or-die.h\"\n \n+KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n+\tstruct object_directory *, 1, fspathhash, fspatheq)\n+\n /*\n  * This is meant to hold a *small* number of objects that you would\n  * want repo_read_object_file() to be able to return, but yet you do not want\ndiff --git a/object-store.h b/object-store.h\nindex 1b3e3d7d014..46961dc9542 100644\n--- a/object-store.h\n+++ b/object-store.h\n@@ -1,11 +1,517 @@\n #ifndef OBJECT_STORE_H\n #define OBJECT_STORE_H\n \n-#include \"khash.h\"\n-#include \"dir.h\"\n-#include \"object-store-ll.h\"\n+#include \"hashmap.h\"\n+#include \"object.h\"\n+#include \"list.h\"\n+#include \"oidset.h\"\n+#include \"thread-utils.h\"\n \n-KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n-\tstruct object_directory *, 1, fspathhash, fspatheq)\n+struct oidmap;\n+struct oidtree;\n+struct strbuf;\n+struct repository;\n+\n+struct object_directory {\n+\tstruct object_directory *next;\n+\n+\t/*\n+\t * Used to store the results of readdir(3) calls when we are OK\n+\t * sacrificing accuracy due to races for speed. That includes\n+\t * object existence with OBJECT_INFO_QUICK, as well as\n+\t * our search for unique abbreviated hashes. Don't use it for tasks\n+\t * requiring greater accuracy!\n+\t *\n+\t * Be sure to call odb_load_loose_cache() before using.\n+\t */\n+\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n+\tstruct oidtree *loose_objects_cache;\n+\n+\t/* Map between object IDs for loose objects. */\n+\tstruct loose_object_map *loose_map;\n+\n+\t/*\n+\t * This is a temporary object store created by the tmp_objdir\n+\t * facility. Disable ref updates since the objects in the store\n+\t * might be discarded on rollback.\n+\t */\n+\tint disable_ref_updates;\n+\n+\t/*\n+\t * This object store is ephemeral, so there is no need to fsync.\n+\t */\n+\tint will_destroy;\n+\n+\t/*\n+\t * Path to the alternative object store. If this is a relative path,\n+\t * it is relative to the current working directory.\n+\t */\n+\tchar *path;\n+};\n+\n+void prepare_alt_odb(struct repository *r);\n+int has_alt_odb(struct repository *r);\n+char *compute_alternate_path(const char *path, struct strbuf *err);\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n+typedef int alt_odb_fn(struct object_directory *, void *);\n+int foreach_alt_odb(alt_odb_fn, void*);\n+typedef void alternate_ref_fn(const struct object_id *oid, void *);\n+void for_each_alternate_ref(alternate_ref_fn, void *);\n+\n+/*\n+ * Add the directory to the on-disk alternates file; the new entry will also\n+ * take effect in the current process.\n+ */\n+void add_to_alternates_file(const char *dir);\n+\n+/*\n+ * Add the directory to the in-memory list of alternates (along with any\n+ * recursive alternates it points to), but do not modify the on-disk alternates\n+ * file.\n+ */\n+void add_to_alternates_memory(const char *dir);\n+\n+/*\n+ * Replace the current writable object directory with the specified temporary\n+ * object directory; returns the former primary object directory.\n+ */\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n+\n+/*\n+ * Restore a previous ODB replaced by set_temporary_main_odb.\n+ */\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n+\n+/*\n+ * Populate and return the loose object cache array corresponding to the\n+ * given object ID.\n+ */\n+struct oidtree *odb_loose_cache(struct object_directory *odb,\n+\t\t\t\t  const struct object_id *oid);\n+\n+/* Empty the loose object cache for the specified object directory. */\n+void odb_clear_loose_cache(struct object_directory *odb);\n+\n+struct packed_git {\n+\tstruct hashmap_entry packmap_ent;\n+\tstruct packed_git *next;\n+\tstruct list_head mru;\n+\tstruct pack_window *windows;\n+\toff_t pack_size;\n+\tconst void *index_data;\n+\tsize_t index_size;\n+\tuint32_t num_objects;\n+\tsize_t crc_offset;\n+\tstruct oidset bad_objects;\n+\tint index_version;\n+\ttime_t mtime;\n+\tint pack_fd;\n+\tint index;              /* for builtin/pack-objects.c */\n+\tunsigned pack_local:1,\n+\t\t pack_keep:1,\n+\t\t pack_keep_in_core:1,\n+\t\t freshened:1,\n+\t\t do_not_close:1,\n+\t\t pack_promisor:1,\n+\t\t multi_pack_index:1,\n+\t\t is_cruft:1;\n+\tunsigned char hash[GIT_MAX_RAWSZ];\n+\tstruct revindex_entry *revindex;\n+\tconst uint32_t *revindex_data;\n+\tconst uint32_t *revindex_map;\n+\tsize_t revindex_size;\n+\t/*\n+\t * mtimes_map points at the beginning of the memory mapped region of\n+\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n+\t * of that .mtimes file\n+\t */\n+\tconst uint32_t *mtimes_map;\n+\tsize_t mtimes_size;\n+\n+\t/* repo denotes the repository this packfile belongs to */\n+\tstruct repository *repo;\n+\n+\t/* something like \".git/objects/pack/xxxxx.pack\" */\n+\tchar pack_name[FLEX_ARRAY]; /* more */\n+};\n+\n+struct multi_pack_index;\n+\n+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n+\t\t\t\t     const struct hashmap_entry *entry,\n+\t\t\t\t     const struct hashmap_entry *entry2,\n+\t\t\t\t     const void *keydata)\n+{\n+\tconst char *key = keydata;\n+\tconst struct packed_git *pg1, *pg2;\n+\n+\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n+\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n+\n+\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n+}\n+\n+struct cached_object_entry;\n+\n+struct raw_object_store {\n+\t/*\n+\t * Set of all object directories; the main directory is first (and\n+\t * cannot be NULL after initialization). Subsequent directories are\n+\t * alternates.\n+\t */\n+\tstruct object_directory *odb;\n+\tstruct object_directory **odb_tail;\n+\tstruct kh_odb_path_map *odb_by_path;\n+\n+\tint loaded_alternates;\n+\n+\t/*\n+\t * A list of alternate object directories loaded from the environment;\n+\t * this should not generally need to be accessed directly, but will\n+\t * populate the \"odb\" list when prepare_alt_odb() is run.\n+\t */\n+\tchar *alternate_db;\n+\n+\t/*\n+\t * Objects that should be substituted by other objects\n+\t * (see git-replace(1)).\n+\t */\n+\tstruct oidmap *replace_map;\n+\tunsigned replace_map_initialized : 1;\n+\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n+\n+\tstruct commit_graph *commit_graph;\n+\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c and midx.c\n+\t */\n+\tstruct multi_pack_index *multi_pack_index;\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c\n+\t */\n+\n+\tstruct packed_git *packed_git;\n+\t/* A most-recently-used ordered version of the packed_git list. */\n+\tstruct list_head packed_git_mru;\n+\n+\tstruct {\n+\t\tstruct packed_git **packs;\n+\t\tunsigned flags;\n+\t} kept_pack_cache;\n+\n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n+\t/*\n+\t * A map of packfiles to packed_git structs for tracking which\n+\t * packs have been loaded already.\n+\t */\n+\tstruct hashmap pack_map;\n+\n+\t/*\n+\t * A fast, rough count of the number of objects in the repository.\n+\t * These two fields are not meant for direct access. Use\n+\t * repo_approximate_object_count() instead.\n+\t */\n+\tunsigned long approximate_object_count;\n+\tunsigned approximate_object_count_valid : 1;\n+\n+\t/*\n+\t * Whether packed_git has already been populated with this repository's\n+\t * packs.\n+\t */\n+\tunsigned packed_git_initialized : 1;\n+};\n+\n+struct raw_object_store *raw_object_store_new(void);\n+void raw_object_store_clear(struct raw_object_store *o);\n+\n+/*\n+ * Create a temporary file rooted in the object database directory, or\n+ * die on failure. The filename is taken from \"pattern\", which should have the\n+ * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n+ * \"template\" buffer. Returns the open descriptor.\n+ */\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n+\n+/*\n+ * Create a pack .keep file named \"name\" (which should generally be the output\n+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n+ * error.\n+ */\n+int odb_pack_keep(const char *name);\n+\n+/*\n+ * Put in `buf` the name of the file in the local object database that\n+ * would be used to store a loose object with the specified oid.\n+ */\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid);\n+\n+void *map_loose_object(struct repository *r, const struct object_id *oid,\n+\t\t       unsigned long *size);\n+\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size);\n+\n+/* Read and unpack an object file into memory, write memory to an object file */\n+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n+\n+void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n+\t\t      unsigned long len, enum object_type type,\n+\t\t      struct object_id *oid);\n+\n+/*\n+ * Add an object file to the in-memory object store, without writing it\n+ * to disk.\n+ *\n+ * Callers are responsible for calling write_object_file to record the\n+ * object in persistent storage before writing any other new objects\n+ * that reference it.\n+ */\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid);\n+\n+struct object_info {\n+\t/* Request */\n+\tenum object_type *typep;\n+\tunsigned long *sizep;\n+\toff_t *disk_sizep;\n+\tstruct object_id *delta_base_oid;\n+\tstruct strbuf *type_name;\n+\tvoid **contentp;\n+\n+\t/* Response */\n+\tenum {\n+\t\tOI_CACHED,\n+\t\tOI_LOOSE,\n+\t\tOI_PACKED,\n+\t\tOI_DBCACHED\n+\t} whence;\n+\tunion {\n+\t\t/*\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } cached;\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } loose;\n+\t\t */\n+\t\tstruct {\n+\t\t\tstruct packed_git *pack;\n+\t\t\toff_t offset;\n+\t\t\tunsigned int is_delta;\n+\t\t} packed;\n+\t} u;\n+};\n+\n+/*\n+ * Initializer for a \"struct object_info\" that wants no items. You may\n+ * also memset() the memory to all-zeroes.\n+ */\n+#define OBJECT_INFO_INIT { 0 }\n+\n+/* Invoke lookup_replace_object() on the given hash */\n+#define OBJECT_INFO_LOOKUP_REPLACE 1\n+/* Allow reading from a loose object file of unknown/bogus type */\n+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n+/* Do not retry packed storage after checking packed and loose storage */\n+#define OBJECT_INFO_QUICK 8\n+/*\n+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n+ * nonzero).\n+ */\n+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n+/*\n+ * This is meant for bulk prefetching of missing blobs in a partial\n+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n+ */\n+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n+\n+/* Die if object corruption (not just an object being missing) was detected. */\n+#define OBJECT_INFO_DIE_IF_CORRUPT 32\n+\n+int oid_object_info_extended(struct repository *r,\n+\t\t\t     const struct object_id *,\n+\t\t\t     struct object_info *, unsigned flags);\n+\n+/* Retry packed storage after checking packed and loose storage */\n+#define HAS_OBJECT_RECHECK_PACKED 1\n+\n+/*\n+ * Returns 1 if the object exists. This function will not lazily fetch objects\n+ * in a partial clone.\n+ */\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags);\n+\n+/*\n+ * These macros and functions are deprecated. If checking existence for an\n+ * object that is likely to be missing and/or whose absence is relatively\n+ * inconsequential (or is consequential but the caller is prepared to handle\n+ * it), use has_object(), which has better defaults (no lazy fetch in a partial\n+ * clone and no rechecking of packed storage). In the unlikely event that a\n+ * caller needs to assert existence of an object that it fully expects to\n+ * exist, and wants to trigger a lazy fetch in a partial clone, use\n+ * oid_object_info_extended() with a NULL struct object_info.\n+ *\n+ * These functions can be removed once all callers have migrated to\n+ * has_object() and/or oid_object_info_extended().\n+ */\n+int repo_has_object_file(struct repository *r, const struct object_id *oid);\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags);\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect);\n+\n+/*\n+ * Enabling the object read lock allows multiple threads to safely call the\n+ * following functions in parallel: repo_read_object_file(),\n+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n+ *\n+ * obj_read_lock() and obj_read_unlock() may also be used to protect other\n+ * section which cannot execute in parallel with object reading. Since the used\n+ * lock is a recursive mutex, these sections can even contain calls to object\n+ * reading functions. However, beware that in these cases zlib inflation won't\n+ * be performed in parallel, losing performance.\n+ *\n+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n+ * any of its callees end up calling it, this recursive call won't benefit from\n+ * parallel inflation.\n+ */\n+void enable_obj_read_lock(void);\n+void disable_obj_read_lock(void);\n+\n+extern int obj_read_use_lock;\n+extern pthread_mutex_t obj_read_mutex;\n+\n+static inline void obj_read_lock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_lock(&obj_read_mutex);\n+}\n+\n+static inline void obj_read_unlock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_unlock(&obj_read_mutex);\n+}\n+\n+/*\n+ * Iterate over the files in the loose-object parts of the object\n+ * directory \"path\", triggering the following callbacks:\n+ *\n+ *  - loose_object is called for each loose object we find.\n+ *\n+ *  - loose_cruft is called for any files that do not appear to be\n+ *    loose objects. Note that we only look in the loose object\n+ *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n+ *    \"objects/foobar\" as cruft.\n+ *\n+ *  - loose_subdir is called for each top-level hashed subdirectory\n+ *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n+ *    after the objects in the directory are processed.\n+ *\n+ * Any callback that is NULL will be ignored. Callbacks returning non-zero\n+ * will end the iteration.\n+ *\n+ * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n+ * scratch buffer, but restored to its original contents before\n+ * the function returns.\n+ */\n+typedef int each_loose_object_fn(const struct object_id *oid,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+typedef int each_loose_cruft_fn(const char *basename,\n+\t\t\t\tconst char *path,\n+\t\t\t\tvoid *data);\n+typedef int each_loose_subdir_fn(unsigned int nr,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n+\t\t\t\tstruct strbuf *path,\n+\t\t\t\teach_loose_object_fn obj_cb,\n+\t\t\t\teach_loose_cruft_fn cruft_cb,\n+\t\t\t\teach_loose_subdir_fn subdir_cb,\n+\t\t\t\tvoid *data);\n+int for_each_loose_file_in_objdir(const char *path,\n+\t\t\t\t  each_loose_object_fn obj_cb,\n+\t\t\t\t  each_loose_cruft_fn cruft_cb,\n+\t\t\t\t  each_loose_subdir_fn subdir_cb,\n+\t\t\t\t  void *data);\n+int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n+\t\t\t\t      each_loose_object_fn obj_cb,\n+\t\t\t\t      each_loose_cruft_fn cruft_cb,\n+\t\t\t\t      each_loose_subdir_fn subdir_cb,\n+\t\t\t\t      void *data);\n+\n+/* Flags for for_each_*_object() below. */\n+enum for_each_object_flags {\n+\t/* Iterate only over local objects, not alternates. */\n+\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n+\n+\t/* Only iterate over packs obtained from the promisor remote. */\n+\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n+\n+\t/*\n+\t * Visit objects within a pack in packfile order rather than .idx order\n+\t */\n+\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n+\n+\t/* Only iterate over packs that are not marked as kept in-core. */\n+\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n+\n+\t/* Only iterate over packs that do not have .keep files. */\n+\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n+};\n+\n+/*\n+ * Iterate over all accessible loose objects without respect to\n+ * reachability. By default, this includes both local and alternate objects.\n+ * The order in which objects are visited is unspecified.\n+ *\n+ * Any flags specific to packs are ignored.\n+ */\n+int for_each_loose_object(each_loose_object_fn, void *,\n+\t\t\t  enum for_each_object_flags flags);\n+\n+/*\n+ * Iterate over all accessible packed objects without respect to reachability.\n+ * By default, this includes both local and alternate packs.\n+ *\n+ * Note that some objects may appear twice if they are found in multiple packs.\n+ * Each pack is visited in an unspecified order. By default, objects within a\n+ * pack are visited in pack-idx order (i.e., sorted by oid).\n+ */\n+typedef int each_packed_object_fn(const struct object_id *oid,\n+\t\t\t\t  struct packed_git *pack,\n+\t\t\t\t  uint32_t pos,\n+\t\t\t\t  void *data);\n+int for_each_object_in_pack(struct packed_git *p,\n+\t\t\t    each_packed_object_fn, void *data,\n+\t\t\t    enum for_each_object_flags flags);\n+int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n+\t\t\t   void *data, enum for_each_object_flags flags);\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n \n #endif /* OBJECT_STORE_H */\ndiff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c\nindex 3e190214d14..609a343ee3e 100644\n--- a/oss-fuzz/fuzz-pack-idx.c\n+++ b/oss-fuzz/fuzz-pack-idx.c\n@@ -1,5 +1,5 @@\n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n \n int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 6a97b52b36d..4c7f7985e78 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -4,7 +4,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 0dbd7c4ffe1..fb68d2ae637 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,7 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\n #include \"config.h\"\ndiff --git a/pack-check.c b/pack-check.c\nindex 95dcbbe9852..874897d6cba 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -8,7 +8,7 @@\n #include \"progress.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct idx_entry {\n \toff_t                offset;\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex bcea28e521d..20900ca88d3 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n \ndiff --git a/pack-objects.h b/pack-objects.h\nindex d73e3843c92..d1c4ae7f9b6 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -1,7 +1,7 @@\n #ifndef PACK_OBJECTS_H\n #define PACK_OBJECTS_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"thread-utils.h\"\n #include \"pack.h\"\n \ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 1ee7b49e206..37ad9c79268 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\ndiff --git a/packfile.c b/packfile.c\nindex 9d09f8bc726..d91016f1c7f 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -19,7 +19,7 @@\n #include \"tree-walk.h\"\n #include \"tree.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"midx.h\"\n #include \"commit-graph.h\"\n #include \"pack-revindex.h\"\ndiff --git a/path.c b/path.c\nindex 910756c8b32..1f7a014322c 100644\n--- a/path.c\n+++ b/path.c\n@@ -15,7 +15,7 @@\n #include \"submodule-config.h\"\n #include \"path.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"lockfile.h\"\n #include \"exec-cmd.h\"\n \ndiff --git a/promisor-remote.c b/promisor-remote.c\nindex 5801ebfd9b2..9d058586dfa 100644\n--- a/promisor-remote.c\n+++ b/promisor-remote.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"config.h\"\n #include \"trace2.h\"\ndiff --git a/protocol-caps.c b/protocol-caps.c\nindex 855f279c2f7..9b8db37a210 100644\n--- a/protocol-caps.c\n+++ b/protocol-caps.c\n@@ -6,7 +6,7 @@\n #include \"hash.h\"\n #include \"hex.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"string-list.h\"\n #include \"strbuf.h\"\ndiff --git a/prune-packed.c b/prune-packed.c\nindex 7dad2fc0c16..c1d95a519d7 100644\n--- a/prune-packed.c\n+++ b/prune-packed.c\n@@ -2,7 +2,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"progress.h\"\n #include \"prune-packed.h\"\ndiff --git a/reachable.c b/reachable.c\nindex 1b26b9b1d76..16e23a38037 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -14,7 +14,7 @@\n #include \"list-objects.h\"\n #include \"packfile.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"pack-mtimes.h\"\n #include \"config.h\"\ndiff --git a/read-cache.c b/read-cache.c\nindex 9909b56902f..9aef59cab4c 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -22,7 +22,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"tree.h\"\n #include \"commit.h\"\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 6da8d4c03b6..7a274633cfc 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"wildmatch.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/reflog.c b/reflog.c\nindex 1b5f031f6d7..60834a124d9 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"reflog.h\"\n #include \"refs.h\"\n #include \"revision.h\"\ndiff --git a/refs.c b/refs.c\nindex 1208f86629b..14f25c499cd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"hook.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"path.h\"\n #include \"submodule.h\"\ndiff --git a/remote.c b/remote.c\nindex e609cf5c56a..7fc657d98b8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"commit.h\"\n #include \"diff.h\"\ndiff --git a/replace-object.c b/replace-object.c\nindex 9a3cdd809a9..7b8a09b5cb4 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -2,7 +2,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"oidmap.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/replace-object.h b/replace-object.h\nindex 66c41b938b4..ba478eb30c4 100644\n--- a/replace-object.h\n+++ b/replace-object.h\n@@ -3,7 +3,7 @@\n \n #include \"oidmap.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct replace_object {\n \tstruct oidmap_entry original;\ndiff --git a/repository.c b/repository.c\nindex 6cbaf2e3daa..9b3d6665fc6 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\ndiff --git a/rerere.c b/rerere.c\nindex 740e8ad1a0b..28e59c42380 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -18,7 +18,7 @@\n #include \"path.h\"\n #include \"pathspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strmap.h\"\n \n #define RESOLVED 0\ndiff --git a/revision.c b/revision.c\nindex b536c4a29ad..352e18b1f97 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -8,7 +8,7 @@\n #include \"hex.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"tag.h\"\n #include \"blob.h\"\ndiff --git a/send-pack.c b/send-pack.c\nindex 856a65d5f5a..5005689cb55 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"run-command.h\"\ndiff --git a/sequencer.c b/sequencer.c\nindex c112d2e1c43..925cfa1af68 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -13,7 +13,7 @@\n #include \"dir.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"pager.h\"\n #include \"commit.h\"\ndiff --git a/server-info.c b/server-info.c\nindex 1ca0e00d51e..3048e5ee407 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -11,7 +11,7 @@\n #include \"packfile.h\"\n #include \"path.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"server-info.h\"\n #include \"strbuf.h\"\n #include \"tempfile.h\"\ndiff --git a/shallow.c b/shallow.c\nindex 06c3266a3e0..2f82ebd6e3f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -5,7 +5,7 @@\n #include \"repository.h\"\n #include \"tempfile.h\"\n #include \"lockfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"pkt-line.h\"\ndiff --git a/streaming.c b/streaming.c\nindex 018b794d252..127d6b5d6ac 100644\n--- a/streaming.c\n+++ b/streaming.c\n@@ -10,7 +10,7 @@\n #include \"streaming.h\"\n #include \"repository.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"packfile.h\"\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex d82b404b73e..8630e27947d 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -13,7 +13,7 @@\n #include \"submodule.h\"\n #include \"strbuf.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/submodule.c b/submodule.c\nindex 0821507ecaa..3e52ef3b036 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -27,7 +27,7 @@\n #include \"parse-options.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n #include \"read-cache-ll.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c\nindex f8f9afbb5b1..50f5941bff7 100644\n--- a/t/helper/test-pack-mtimes.c\n+++ b/t/helper/test-pack-mtimes.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"strbuf.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pack-mtimes.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c\nindex a1af9710c31..34f1aee5581 100644\n--- a/t/helper/test-partial-clone.c\n+++ b/t/helper/test-partial-clone.c\n@@ -1,7 +1,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n /*\ndiff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c\nindex 811dde1cb3c..8b413b644be 100644\n--- a/t/helper/test-read-graph.c\n+++ b/t/helper/test-read-graph.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"commit-graph.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"bloom.h\"\n #include \"setup.h\"\n \ndiff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c\nindex fc632369618..ac81390899a 100644\n--- a/t/helper/test-read-midx.c\n+++ b/t/helper/test-read-midx.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"midx.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"packfile.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 2ff67c067ac..4cfc7c90b59 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -5,7 +5,7 @@\n #include \"refs.h\"\n #include \"setup.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\ndiff --git a/tag.c b/tag.c\nindex 8d9e9e29304..05be39067cf 100644\n--- a/tag.c\n+++ b/tag.c\n@@ -5,7 +5,7 @@\n #include \"environment.h\"\n #include \"tag.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"blob.h\"\ndiff --git a/tmp-objdir.c b/tmp-objdir.c\nindex 31d16a4c2c5..c38fbeb5e8a 100644\n--- a/tmp-objdir.c\n+++ b/tmp-objdir.c\n@@ -10,7 +10,7 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n #include \"quote.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n struct tmp_objdir {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a0333979656..90655d52378 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -6,7 +6,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace2.h\"\n #include \"tree.h\"\n #include \"pathspec.h\"\ndiff --git a/tree.c b/tree.c\nindex ad86ad1ba99..b85f56267fb 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"tree.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"alloc.h\"\n #include \"tree-walk.h\"\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex cf5b73c84be..471837f0329 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -26,7 +26,7 @@\n #include \"symlinks.h\"\n #include \"trace2.h\"\n #include \"fsmonitor.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 02ce6336028..30e4630f3a1 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -10,7 +10,7 @@\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/walker.c b/walker.c\nindex 1cf3da02193..4fedc19f346 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"walker.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"strbuf.h\"\n #include \"tree.h\"\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 77712811ff1..1edcd319e6e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"config.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"xdiff-interface.h\"\n #include \"xdiff/xtypes.h\"\n\n-- \n2.49.0.777.g153de2bbd5.dirty\n\n"},{"id":"516002","messageId":"CABPp-BHJHPx7orf-jjgbcPtJo=tGeDZzYWEKvPU-qzXTa1fNSw@mail.gmail.com","threadId":"63270","inReplyTo":"Z_jgdV7Tkw0hkvgj@pks.im","subject":"Re: [PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-04-11T17:11:52Z","receivedAt":"2025-04-11T17:12:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Apr 11, 2025 at 2:27 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> On Wed, Apr 09, 2025 at 07:36:47AM -0700, Elijah Newren wrote:\n> > On Tue, Apr 8, 2025 at 3:37 AM Patrick Steinhardt <ps@pks.im> wrote:\n> > >\n> > > The `safe_create_leading_directories()` function and its relatives\n> >\n> > How is mkdir_in_gitdir() a relative of safe_create_leading_directories()?\n> >\n> > I assumed the relation was \"called by\", but there is no such\n> > relationship.  The rest of the patch looked fine, but I was puzzled\n> > for a while trying to figure out what this relationship is.\n>\n> It's more of a sibling than a child/parent in this case, true. I still\n> think it makes sense to move it around as it is rather generic in the\n> functionality it provides and doesn't have anything to do with objects.\n>\n> Patrick\n\nI fully agree it makes sense to move it and that dir.c is a good place\nfor it, I just think it also makes sense to fix the commit message to\navoid the misleading/confusing text by calling out mkdir_in_gitdir()\nseparately since it isn't related to\nsafe_create_leading_directories().  For example, highlighting the text\nI added between asterisks, you could make it read:\n\nThe `safe_create_leading_directories()` function and its relatives*,\nas well as mkdir_in_gitdir()*, are\nlocated in \"object-file.c\", which is not a good fit as they provide\ngeneric functionality not related to objects at all. Move them into\n\"dir.c\".\n\nHowever, this is a nitpick and probably not worth another re-roll;\nespecially since everything else in your v2 looks great to me.\n"},{"id":"516012","messageId":"xmqq5xjampdc.fsf@gitster.g","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-1-2bea0c9033ae@pks.im","subject":"Re: [PATCH v2 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-11T20:09:51Z","receivedAt":"2025-04-11T20:09:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The `safe_create_leading_directories()` function and its relatives are\n> located in \"object-file.c\", which is not a good fit as they provide\n> generic functionality not related to objects at all. Move them into\n> \"dir.c\".\n\nIt may be debatable that <dir.c>, which has traditionally been a\ncollection of read-only operations (mostly for exclude/ignore\nprocessing), is a good place to host \"mkdir -p\", but it certainly is\nbetter than having it in <object-file.c>\n\nLooking good.\n"},{"id":"516013","messageId":"CAPig+cQ5AEwTaK2LYkV39yrZppnF_MMMnB2V0nTGWZ-PcptVYA@mail.gmail.com","threadId":"63270","inReplyTo":"xmqq5xjampdc.fsf@gitster.g","subject":"Re: [PATCH v2 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2025-04-11T21:29:13Z","receivedAt":"2025-04-11T21:29:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Apr 11, 2025 at 4:10 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > The `safe_create_leading_directories()` function and its relatives are\n> > located in \"object-file.c\", which is not a good fit as they provide\n> > generic functionality not related to objects at all. Move them into\n> > \"dir.c\".\n>\n> It may be debatable that <dir.c>, which has traditionally been a\n> collection of read-only operations (mostly for exclude/ignore\n> processing), is a good place to host \"mkdir -p\", but it certainly is\n> better than having it in <object-file.c>\n\nI probably would have expected safe_create_leading_directories() to be\nmoved to \"path.[hc]\" which already houses functions such as\nsafe_create_dir(), normalize_path_copy(), ends_with_path_components(),\nlongest_ancestor_length(), etc.\n"},{"id":"516014","messageId":"xmqqtt6ul30k.fsf@gitster.g","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-8-2bea0c9033ae@pks.im","subject":"Re: [PATCH v2 8/9] object-store: remove global array of cached objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-11T22:58:03Z","receivedAt":"2025-04-11T22:58:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Cached objects are virtual objects that can be set up without writing\n> anything into the object store directly. This mechanism for example\n> allows us to create fake commits in git-blame(1).\n>\n> The cached objects are stored in a global variable. Refactor the code so\n> that we instead store the array as part of the raw object store. This is\n> another step into the direction of libifying our object database.\n\nWhile we do need some execution context object to hang these virtual\nobjects, once we decide that it cannot be global, I am not sure if\nepository objects are good home for them.  If your application\nrunning in a repository needs to give one object name to a virtual\nobject, and then that same application wants to access a submodule\nof that repository in the same process image, wouldn't you have one\nin-core repository object for the top-level superproject, and one\nfor each submodule?  If a submodule commit bound to a path in the\nsuperproject's tree is a viertual \"pretend\" commit object or if it\nhas a virtual \"pretend\" tree object, don't you need to expose these\nto both submodule and superproject repositories, if your application\nwants to seamlessly cross the module boundary (think \"git grep\n--recurse-submodules\" or something)?\n\nFor now, as long as the_repository is being used as that \"execution\ncontext object\", and not a repository instance passed along the call\nchain, then the globalness of these virtual objects is maintained,\nso this change will not cause breakage (e.g., such an application\nmay want to pick up the virtual object from the repository instance\nfor the superproject and it may find it, but when traversing down to\na submdoule, the same virtual object may not be found in the\nrepository instance for the submodule it descended into and working\nin, if you make it per repository and pass repository instance\naround along the call chain).  But eventually somebody will start\nsaying \"let's remove USE_THE_REPOSITORY_VARIABLE\", at which point I\nam not sure how subtle such a bug would become.\n"},{"id":"516030","messageId":"20250412081724.GA109770@coredump.intra.peff.net","threadId":"63270","inReplyTo":"20250411-pks-split-object-file-v2-6-2bea0c9033ae@pks.im","subject":"Re: [PATCH v2 6/9] object-file: split out functions relating to index subsystem","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-04-12T08:17:24Z","receivedAt":"2025-04-12T08:17:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Apr 11, 2025 at 11:29:55AM +0200, Patrick Steinhardt wrote:\n\n> Split out functions relating to the index subsystem from \"object-file.c\"\n> to help us separate concerns.\n\nI know these functions all start with \"index_\", and they do take an\nindex_state variable, but I'm not sure they are really about Git's index\nsubsystem at all.\n\nThe term \"index\" here is more about \"compute the sha1 index of the\ncontent\". E.g., the function index_path() goes all the way back to\nec1fcc16af (Show original and resulting blob object info in diff\noutput., 2005-10-07)!\n\nBack then it did not take an index struct, or even care about having an\nindex at all. Later, they learned to call convert_to_git() in 6c510bee20\n(Lazy man's auto-CRLF, 2007-02-13). And that function may check the\nindex for .gitattributes files.\n\nIt originally just used the global the_index variable for that, but\nlater commits like 58bf2a4cc7 (sha1-file.c: remove implicit dependency\non the_index, 2018-09-21) passed the istate around the call stack.\n\nSo having access to an index struct is mostly incidental to these\nfunctions. Which makes sense looking at the callers: there are many\npure-object operations that would work without an index (or even a repo\nin some cases!) like hash-object, git-replace, diff.\n\n  Side note: I'm actually not even sure we would read attributes from\n  the index, since we don't set GIT_ATTR_INDEX. So I wondered if we\n  could simply pass NULL to convert_to_git() here. But I think these\n  days some of the \"auto\" CRLF modes also have heuristics based on\n  what's the content we find in the index for that path. See\n  has_crlf_in_index() and its callers.\n\n\nSo it seems to me that these really are more about creating objects than\nthey are about the index. I don't mind splitting them out, but it seems\nlike they're equally weird in read-cache.[ch].\n\n-Peff\n"},{"id":"516091","messageId":"xmqqcydfm081.fsf@gitster.g","threadId":"63270","inReplyTo":"20250412081724.GA109770@coredump.intra.peff.net","subject":"Re: [PATCH v2 6/9] object-file: split out functions relating to index subsystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-14T11:49:50Z","receivedAt":"2025-04-14T11:49:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> So it seems to me that these really are more about creating objects than\n> they are about the index. I don't mind splitting them out, but it seems\n> like they're equally weird in read-cache.[ch].\n\nThanks for a good observation based on history of the code involved.\n"},{"id":"516163","messageId":"Z_4kkQ6IqQ-CDUO4@pks.im","threadId":"63270","inReplyTo":"xmqqtt6ul30k.fsf@gitster.g","subject":"Re: [PATCH v2 8/9] object-store: remove global array of cached objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:19:13Z","receivedAt":"2025-04-15T09:19:20Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Apr 11, 2025 at 03:58:03PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > Cached objects are virtual objects that can be set up without writing\n> > anything into the object store directly. This mechanism for example\n> > allows us to create fake commits in git-blame(1).\n> >\n> > The cached objects are stored in a global variable. Refactor the code so\n> > that we instead store the array as part of the raw object store. This is\n> > another step into the direction of libifying our object database.\n> \n> While we do need some execution context object to hang these virtual\n> objects, once we decide that it cannot be global, I am not sure if\n> epository objects are good home for them.  If your application\n> running in a repository needs to give one object name to a virtual\n> object, and then that same application wants to access a submodule\n> of that repository in the same process image, wouldn't you have one\n> in-core repository object for the top-level superproject, and one\n> for each submodule?  If a submodule commit bound to a path in the\n> superproject's tree is a viertual \"pretend\" commit object or if it\n> has a virtual \"pretend\" tree object, don't you need to expose these\n> to both submodule and superproject repositories, if your application\n> wants to seamlessly cross the module boundary (think \"git grep\n> --recurse-submodules\" or something)?\n> \n> For now, as long as the_repository is being used as that \"execution\n> context object\", and not a repository instance passed along the call\n> chain, then the globalness of these virtual objects is maintained,\n> so this change will not cause breakage (e.g., such an application\n> may want to pick up the virtual object from the repository instance\n> for the superproject and it may find it, but when traversing down to\n> a submdoule, the same virtual object may not be found in the\n> repository instance for the submodule it descended into and working\n> in, if you make it per repository and pass repository instance\n> around along the call chain).  But eventually somebody will start\n> saying \"let's remove USE_THE_REPOSITORY_VARIABLE\", at which point I\n> am not sure how subtle such a bug would become.\n\nI think the answer is very much \"it depends\". I can think of usecases\nwhere it might be the right to pretend objects to exist globally, but\nthere's also usecases where I think it makes sense to treat them as\nrepository-specific. The thing is: we can do the former if the virtual\nobjects are specific to a repository, but we can't do the latter if the\nvirtual objects are global.\n\nAs far as I can see we only use this mechanism in git-blame(1) right now\nto create a fake working tree commit. This mechanism does not cross into\nsubmodules at all, and if it would I think we would want to create two\nseparate fake working tree commits anyway: one for the parent\nrepository, and one for each submodule. So converting this mechanism to\nbe local to the repository (or rather local to an object store) feels\nlike the right thing to do to me.\n\nBut I agree with you in principle: we will have to be a lot more mindful\ngoing forward as it comes to handling multiple repositories in-memory.\nWe don't do this well right now, but as we convert more and more code so\nthat it doesn't use `the_repository` anymore we'll have to become better\nat this indeed. From my perspective that isn't only true for these fake\nworking tree commits, but it's a general thing that we'll have to sort\nout over time. It's inherent to the whole libifcation process.\n\nI think for the most part we're fine right now, as we don't make use of\nany of the new capabilities that libifcation brings with it in theory.\nBut once usecases start to come up that _do_ make use of this we will\nhave to think about those issues a whole lot more carefully.\n\nPatrick\n"},{"id":"516164","messageId":"Z_4klRXH1wwv7JXJ@pks.im","threadId":"63270","inReplyTo":"CAPig+cQ5AEwTaK2LYkV39yrZppnF_MMMnB2V0nTGWZ-PcptVYA@mail.gmail.com","subject":"Re: [PATCH v2 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:19:17Z","receivedAt":"2025-04-15T09:19:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Apr 11, 2025 at 05:29:13PM -0400, Eric Sunshine wrote:\n> On Fri, Apr 11, 2025 at 4:10 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > Patrick Steinhardt <ps@pks.im> writes:\n> > > The `safe_create_leading_directories()` function and its relatives are\n> > > located in \"object-file.c\", which is not a good fit as they provide\n> > > generic functionality not related to objects at all. Move them into\n> > > \"dir.c\".\n> >\n> > It may be debatable that <dir.c>, which has traditionally been a\n> > collection of read-only operations (mostly for exclude/ignore\n> > processing), is a good place to host \"mkdir -p\", but it certainly is\n> > better than having it in <object-file.c>\n> \n> I probably would have expected safe_create_leading_directories() to be\n> moved to \"path.[hc]\" which already houses functions such as\n> safe_create_dir(), normalize_path_copy(), ends_with_path_components(),\n> longest_ancestor_length(), etc.\n\nAh, good catch! Will adapt.\n\nPatrick\n"},{"id":"516165","messageId":"Z_4kmF8NjtERSsBz@pks.im","threadId":"63270","inReplyTo":"CABPp-BHJHPx7orf-jjgbcPtJo=tGeDZzYWEKvPU-qzXTa1fNSw@mail.gmail.com","subject":"Re: [PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:19:20Z","receivedAt":"2025-04-15T09:19:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Apr 11, 2025 at 10:11:52AM -0700, Elijah Newren wrote:\n> On Fri, Apr 11, 2025 at 2:27 AM Patrick Steinhardt <ps@pks.im> wrote:\n> >\n> > On Wed, Apr 09, 2025 at 07:36:47AM -0700, Elijah Newren wrote:\n> > > On Tue, Apr 8, 2025 at 3:37 AM Patrick Steinhardt <ps@pks.im> wrote:\n> > > >\n> > > > The `safe_create_leading_directories()` function and its relatives\n> > >\n> > > How is mkdir_in_gitdir() a relative of safe_create_leading_directories()?\n> > >\n> > > I assumed the relation was \"called by\", but there is no such\n> > > relationship.  The rest of the patch looked fine, but I was puzzled\n> > > for a while trying to figure out what this relationship is.\n> >\n> > It's more of a sibling than a child/parent in this case, true. I still\n> > think it makes sense to move it around as it is rather generic in the\n> > functionality it provides and doesn't have anything to do with objects.\n> >\n> > Patrick\n> \n> I fully agree it makes sense to move it and that dir.c is a good place\n> for it, I just think it also makes sense to fix the commit message to\n> avoid the misleading/confusing text by calling out mkdir_in_gitdir()\n> separately since it isn't related to\n> safe_create_leading_directories().  For example, highlighting the text\n> I added between asterisks, you could make it read:\n> \n> The `safe_create_leading_directories()` function and its relatives*,\n> as well as mkdir_in_gitdir()*, are\n> located in \"object-file.c\", which is not a good fit as they provide\n> generic functionality not related to objects at all. Move them into\n> \"dir.c\".\n> \n> However, this is a nitpick and probably not worth another re-roll;\n> especially since everything else in your v2 looks great to me.\n\nEric has suggested moving it into \"path.c\", which I think is indeed a\nbetter fix. I'm using that as an opportunity to rename the function to\n`safe_create_dir_in_gitdir()` so that it matches `safe_create_dir()`,\nwhich is functionally similar. And because \"path.c\" does not depend on\n`the_repository` anymore I'll also inject a repository via a parameter.\n\nAll to say: there's a bunch of additional changes now, so I'll split\nthis out into a separate commit.\n\nPatrick\n"},{"id":"516166","messageId":"Z_4kmxMhCnsRyPwn@pks.im","threadId":"63270","inReplyTo":"20250412081724.GA109770@coredump.intra.peff.net","subject":"Re: [PATCH v2 6/9] object-file: split out functions relating to index subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:19:23Z","receivedAt":"2025-04-15T09:19:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Sat, Apr 12, 2025 at 04:17:24AM -0400, Jeff King wrote:\n> On Fri, Apr 11, 2025 at 11:29:55AM +0200, Patrick Steinhardt wrote:\n> \n> > Split out functions relating to the index subsystem from \"object-file.c\"\n> > to help us separate concerns.\n> \n> I know these functions all start with \"index_\", and they do take an\n> index_state variable, but I'm not sure they are really about Git's index\n> subsystem at all.\n> \n> The term \"index\" here is more about \"compute the sha1 index of the\n> content\". E.g., the function index_path() goes all the way back to\n> ec1fcc16af (Show original and resulting blob object info in diff\n> output., 2005-10-07)!\n> \n> Back then it did not take an index struct, or even care about having an\n> index at all. Later, they learned to call convert_to_git() in 6c510bee20\n> (Lazy man's auto-CRLF, 2007-02-13). And that function may check the\n> index for .gitattributes files.\n> \n> It originally just used the global the_index variable for that, but\n> later commits like 58bf2a4cc7 (sha1-file.c: remove implicit dependency\n> on the_index, 2018-09-21) passed the istate around the call stack.\n> \n> So having access to an index struct is mostly incidental to these\n> functions. Which makes sense looking at the callers: there are many\n> pure-object operations that would work without an index (or even a repo\n> in some cases!) like hash-object, git-replace, diff.\n> \n>   Side note: I'm actually not even sure we would read attributes from\n>   the index, since we don't set GIT_ATTR_INDEX. So I wondered if we\n>   could simply pass NULL to convert_to_git() here. But I think these\n>   days some of the \"auto\" CRLF modes also have heuristics based on\n>   what's the content we find in the index for that path. See\n>   has_crlf_in_index() and its callers.\n> \n> \n> So it seems to me that these really are more about creating objects than\n> they are about the index. I don't mind splitting them out, but it seems\n> like they're equally weird in read-cache.[ch].\n\nYeah, I was too trigger-happy in this case indeed. I'll retain the code\nas-is for now, thanks!\n\nPatrick\n"},{"id":"516168","messageId":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im","subject":"[PATCH v3 00/10] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:13Z","receivedAt":"2025-04-15T09:38:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\n\"object-file.c\" is quite a grab-bag of all kinds of different functions.\nMany of these functions aren't really a good fit though and should be\nowned by a different subsystem. This patch series tries to split up\nconcerns a bit better by splitting out this functionality into other\nfiles:\n\n  - `safe_create_leading_directories()` is moved into \"dir.c\".\n  - `xmmap()` is moved into \"wrapper.c\".\n  - `git_open_cloexec()` is moved into \"compat/open.c\".\n  - Several functions attached to `struct index_state` are moved into\n    \"read-cache.c\".\n  - Several functions related to `struct object_store` are moved into a\n    new file \"object-store.c\".\n\n\"object-file.c\" now mostly contains logic to read and write loose object\nfiles, whereas \"object-store.c\" contains the higher-level logic to\nmanage different object directories for a repository. Eventually, these\nwill become the loose object backend as well as the `struct ref_store`\nequivalent for objects, respectively.\n\nThe series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\nwith ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\nbranch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\ninto it.\n\nChanges in v2:\n  - Fix a grammar issue in one of the commit messages.\n  - Link to v1: https://lore.kernel.org/r/20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im\n\nChanges in v3:\n  - Rename `mkdir_in_gitdir()` to `safe_create_dir_in_gitdir()` to match\n    naming of similar functions.\n  - Move `safe_create_leading_directories()` et al into \"path.c\" instead\n    of into \"dir.c\". This also requires us to start injecting a repo via\n    parameters as \"path.c\" doesn't have `the_repository` available\n    anymore.\n  - Drop the commit that moves `index_blob_stream()` and related\n    functions.\n  - Expand the reasoning why we want to have cached objects per object\n    store instead of globally.\n  - Drop `index_blob_stream()`, which is a trivial wrapper around\n    `index_blob_bulk_checkin()`.\n  - Link to v2: https://lore.kernel.org/r/20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (10):\n      object-file: move `mkdir_in_gitdir()` into \"path.c\"\n      object-file: move `safe_create_leading_directories()` into \"path.c\"\n      object-file: move `git_open_cloexec()` to \"compat/open.c\"\n      object-file: move `xmmap()` into \"wrapper.c\"\n      object-file: split out functions relating to object store subsystem\n      object-file: split up concerns of `HASH_*` flags\n      object-file: drop `index_blob_stream()`\n      object: split out functions relating to object store subsystem\n      object-store: remove global array of cached objects\n      object-store: merge \"object-store-ll.h\" and \"object-store.h\"\n\n Makefile                           |    3 +-\n apply.c                            |    2 +-\n archive-tar.c                      |    2 +-\n archive-zip.c                      |    2 +-\n archive.c                          |    2 +-\n attr.c                             |    2 +-\n bisect.c                           |    2 +-\n blame.c                            |    4 +-\n builtin/backfill.c                 |    2 +-\n builtin/blame.c                    |    2 +-\n builtin/bugreport.c                |    4 +-\n builtin/cat-file.c                 |    2 +-\n builtin/checkout.c                 |    3 +-\n builtin/clone.c                    |    6 +-\n builtin/commit-graph.c             |    2 +-\n builtin/commit-tree.c              |    2 +-\n builtin/count-objects.c            |    2 +-\n builtin/credential-cache--daemon.c |    4 +-\n builtin/describe.c                 |    2 +-\n builtin/diagnose.c                 |    4 +-\n builtin/difftool.c                 |   31 +-\n builtin/fast-export.c              |    2 +-\n builtin/fast-import.c              |    4 +-\n builtin/fetch.c                    |    2 +-\n builtin/fsck.c                     |    4 +-\n builtin/gc.c                       |    9 +-\n builtin/grep.c                     |    2 +-\n builtin/hash-object.c              |   25 +-\n builtin/index-pack.c               |    2 +-\n builtin/init-db.c                  |    3 +-\n builtin/log.c                      |    6 +-\n builtin/ls-tree.c                  |    2 +-\n builtin/merge-file.c               |    1 +\n builtin/merge-tree.c               |    2 +-\n builtin/mktag.c                    |    2 +-\n builtin/mktree.c                   |    3 +-\n builtin/multi-pack-index.c         |    2 +-\n builtin/mv.c                       |    3 +-\n builtin/notes.c                    |    3 +-\n builtin/pack-objects.c             |    2 +-\n builtin/pack-redundant.c           |    2 +-\n builtin/prune.c                    |    2 +-\n builtin/rebase.c                   |    3 +-\n builtin/receive-pack.c             |    3 +-\n builtin/remote.c                   |    2 +-\n builtin/repack.c                   |    2 +-\n builtin/replace.c                  |    4 +-\n builtin/rev-list.c                 |    2 +-\n builtin/show-ref.c                 |    2 +-\n builtin/sparse-checkout.c          |    5 +-\n builtin/submodule--helper.c        |    6 +-\n builtin/tag.c                      |    3 +-\n builtin/unpack-file.c              |    3 +-\n builtin/unpack-objects.c           |    3 +-\n builtin/update-index.c             |    2 +-\n builtin/worktree.c                 |    8 +-\n bulk-checkin.c                     |    8 +-\n bulk-checkin.h                     |   15 +\n bundle-uri.c                       |    2 +-\n bundle.c                           |    2 +-\n cache-tree.c                       |    4 +-\n combine-diff.c                     |    2 +-\n commit-graph.c                     |    5 +-\n commit-graph.h                     |    2 +-\n commit.c                           |    3 +-\n compat/open.c                      |   29 +\n config.c                           |    2 +-\n connected.c                        |    2 +-\n convert.c                          |    2 +-\n diagnose.c                         |    2 +-\n diff.c                             |    2 +-\n diffcore-rename.c                  |    2 +-\n dir.c                              |    7 +-\n entry.c                            |    2 +-\n fetch-pack.c                       |    2 +-\n fmt-merge-msg.c                    |    2 +-\n fsck.c                             |    2 +-\n git-compat-util.h                  |    3 +\n grep.c                             |    2 +-\n http-backend.c                     |    2 +-\n http-push.c                        |    3 +-\n http-walker.c                      |    2 +-\n http.c                             |    2 +-\n list-objects-filter.c              |    2 +-\n list-objects.c                     |    2 +-\n log-tree.c                         |    2 +-\n mailmap.c                          |    2 +-\n match-trees.c                      |    3 +-\n merge-blobs.c                      |    2 +-\n merge-ort.c                        |    3 +-\n merge-recursive.c                  |    6 +-\n meson.build                        |    2 +\n midx-write.c                       |    2 +-\n midx.c                             |    1 -\n notes-cache.c                      |    3 +-\n notes-merge.c                      |    8 +-\n notes.c                            |    3 +-\n object-file.c                      | 1220 +-----------------------------------\n object-file.h                      |  121 ++--\n object-name.c                      |    2 +-\n object-store-ll.h                  |  556 ----------------\n object-store.c                     | 1050 +++++++++++++++++++++++++++++++\n object-store.h                     |  516 ++++++++++++++-\n object.c                           |   67 --\n oss-fuzz/fuzz-pack-idx.c           |    2 +-\n pack-bitmap-write.c                |    2 +-\n pack-bitmap.c                      |    3 +-\n pack-check.c                       |    2 +-\n pack-mtimes.c                      |    3 +-\n pack-objects.h                     |    2 +-\n pack-revindex.c                    |    3 +-\n packfile.c                         |    2 +-\n path.c                             |  111 +++-\n path.h                             |   45 ++\n promisor-remote.c                  |    2 +-\n protocol-caps.c                    |    2 +-\n prune-packed.c                     |    2 +-\n reachable.c                        |    2 +-\n read-cache.c                       |    6 +-\n ref-filter.c                       |    2 +-\n reflog.c                           |    2 +-\n refs.c                             |    2 +-\n refs/files-backend.c               |    4 +-\n remote.c                           |    2 +-\n replace-object.c                   |    2 +-\n replace-object.h                   |    2 +-\n repository.c                       |    2 +-\n rerere.c                           |    7 +-\n revision.c                         |    2 +-\n send-pack.c                        |    2 +-\n sequencer.c                        |    6 +-\n server-info.c                      |    4 +-\n shallow.c                          |    2 +-\n streaming.c                        |    2 +-\n submodule-config.c                 |    2 +-\n submodule.c                        |    4 +-\n t/helper/test-pack-mtimes.c        |    2 +-\n t/helper/test-partial-clone.c      |    2 +-\n t/helper/test-read-graph.c         |    2 +-\n t/helper/test-read-midx.c          |    2 +-\n t/helper/test-ref-store.c          |    2 +-\n tag.c                              |    2 +-\n tmp-objdir.c                       |    2 +-\n tree-walk.c                        |    2 +-\n tree.c                             |    2 +-\n unpack-trees.c                     |    2 +-\n upload-pack.c                      |    2 +-\n walker.c                           |    2 +-\n wrapper.c                          |   48 ++\n xdiff-interface.c                  |    2 +-\n 150 files changed, 2147 insertions(+), 2067 deletions(-)\n\nRange-diff versus v2:\n\n 1:  ed337338970 <  -:  ----------- object-file: move `safe_create_leading_directories()` into \"dir.c\"\n -:  ----------- >  1:  8d838f92936 object-file: move `mkdir_in_gitdir()` into \"path.c\"\n -:  ----------- >  2:  c7c723db86c object-file: move `safe_create_leading_directories()` into \"path.c\"\n 2:  48730ada01e =  3:  2b4db7b2090 object-file: move `git_open_cloexec()` to \"compat/open.c\"\n 3:  a9436d9e4a6 =  4:  7e8b750d652 object-file: move `xmmap()` into \"wrapper.c\"\n 4:  628d0bcce6c !  5:  3b90e8a5841 object-file: split out functions relating to object store subsystem\n    @@ object-file.c: static int get_conv_flags(unsigned flags)\n      \t\treturn 0;\n      }\n      \n    --\n     -int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n     -{\n     -\tint fd;\n    @@ object-file.c: static int get_conv_flags(unsigned flags)\n     -\t/* slow path */\n     -\t/* some mkstemp implementations erase temp_filename on failure */\n     -\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n    --\tsafe_create_leading_directories(temp_filename->buf);\n    +-\tsafe_create_leading_directories(the_repository, temp_filename->buf);\n     -\treturn xmkstemp_mode(temp_filename->buf, mode);\n     -}\n     -\n    @@ object-file.c: static int get_conv_flags(unsigned flags)\n     -\t\treturn fd;\n     -\n     -\t/* slow path */\n    --\tsafe_create_leading_directories_const(name);\n    +-\tsafe_create_leading_directories_const(the_repository, name);\n     -\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n     -}\n     -\n    @@ object-store.c (new)\n     +\t/* slow path */\n     +\t/* some mkstemp implementations erase temp_filename on failure */\n     +\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n    -+\tsafe_create_leading_directories(temp_filename->buf);\n    ++\tsafe_create_leading_directories(the_repository, temp_filename->buf);\n     +\treturn xmkstemp_mode(temp_filename->buf, mode);\n     +}\n     +\n    @@ object-store.c (new)\n     +\t\treturn fd;\n     +\n     +\t/* slow path */\n    -+\tsafe_create_leading_directories_const(name);\n    ++\tsafe_create_leading_directories_const(the_repository, name);\n     +\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n     +}\n     +\n 5:  fd102d9e50a =  6:  6bc5d4811d3 object-file: split up concerns of `HASH_*` flags\n 6:  298a867897d <  -:  ----------- object-file: split out functions relating to index subsystem\n -:  ----------- >  7:  c1e323f1673 object-file: drop `index_blob_stream()`\n 7:  53e2bd9d117 =  8:  6b46541fe2a object: split out functions relating to object store subsystem\n 8:  00b4a52b8ce !  9:  59df5f22b9d object-store: remove global array of cached objects\n    @@ Commit message\n         object-store: remove global array of cached objects\n     \n         Cached objects are virtual objects that can be set up without writing\n    -    anything into the object store directly. This mechanism for example\n    -    allows us to create fake commits in git-blame(1).\n    +    anything into the object store directly, which is used by git-blame(1)\n    +    to create fake commits for the working tree.\n     \n    -    The cached objects are stored in a global variable. Refactor the code so\n    -    that we instead store the array as part of the raw object store. This is\n    -    another step into the direction of libifying our object database.\n    +    These cached objects are stored in a global variable, which is another\n    +    roadblock for libification of the object subsystem. Refactor the code so\n    +    that we instead store the array as part of the raw object store.\n    +\n    +    This refactoring raises the question whether virtual objects should\n    +    really be specific to a single repository (or rather a single object\n    +    store). Hypothetical usecases might for example span across submodules,\n    +    and here it may or may not be the right thing to provide virtual objects\n    +    across submodule boundaries.\n    +\n    +    The only existing usecase is git-blame(1) though, which does not know to\n    +    blame across submodule boundaries in the first place. As such, storing\n    +    these objects both globally and per-repository would achieve the same\n    +    result right now. But arguably, if we learned to blame across submodule\n    +    boundaries, we would likely want to create separate fare working tree\n    +    commits for each of the submodules so that the user can learn which\n    +    worktree a specific uncommitted change belongs to. And even if we would\n    +    want to create the same fake commit for each of the submodules we could\n    +    do that when storing separate virtual objects per object store.\n    +\n    +    While this is all rather hypothetical, the takeaway is that handling\n    +    virtual objects per-object store gives us more flexibility compared to\n    +    storing them globally. In a hypothetical future where we have achieved\n    +    full libification one might be able to handle unrelated repositories in\n    +    a single process, where the state of one repository should not have an\n    +    impact on the state of another repository. As such, storing these cached\n    +    objects per object store will enable more usecases and should lead to\n    +    less surprising outcomes overall.\n     \n         Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n 9:  6365dc7cd16 ! 10:  df6a749245f object-store: merge \"object-store-ll.h\" and \"object-store.h\"\n    @@ builtin/replace.c\n      #include \"object-name.h\"\n     -#include \"object-store-ll.h\"\n     +#include \"object-store.h\"\n    - #include \"read-cache.h\"\n      #include \"replace-object.h\"\n      #include \"tag.h\"\n    + #include \"wildmatch.h\"\n     \n      ## builtin/rev-list.c ##\n     @@\n    @@ notes-merge.c\n     -#include \"object-store-ll.h\"\n     +#include \"object-store.h\"\n      #include \"path.h\"\n    - #include \"read-cache.h\"\n      #include \"repository.h\"\n    + #include \"diff.h\"\n     \n      ## object-file.c ##\n     @@\n    - \n      #include \"git-compat-util.h\"\n      #include \"bulk-checkin.h\"\n    + #include \"convert.h\"\n     +#include \"dir.h\"\n      #include \"environment.h\"\n    + #include \"fsck.h\"\n      #include \"gettext.h\"\n    - #include \"hex.h\"\n     \n      ## object-name.c ##\n     @@\n\n---\nbase-commit: 9442b1c919af9aed513eb0a484fe96358a500cf5\nchange-id: 20250408-pks-split-object-file-c61d7cd2a21f\n\n"},{"id":"516169","messageId":"20250415-pks-split-object-file-v3-1-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 01/10] object-file: move `mkdir_in_gitdir()` into \"path.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:14Z","receivedAt":"2025-04-15T09:38:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `mkdir_in_gitdir()` function is similar to `safe_create_dir()`, but\nthe former is hosted in \"object-file.c\" whereas the latter is hosted in\n\"path.c\". The latter code unit makes way more sense though as the logic\nhas nothing to do with object files in particular.\n\nMove the file into \"path.c\". While at it, we:\n\n  - Rename the function to `safe_create_dir_in_gitdir()` so that the\n    function names are similar to one another.\n\n  - Remove the dependency on `the_repository` by making the callers pass\n    the repository instead.\n\nAdjust callers accordingly.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/rebase.c |  3 ++-\n object-file.c    | 33 ++-------------------------------\n object-file.h    |  2 --\n path.c           | 29 +++++++++++++++++++++++++++++\n path.h           | 11 +++++++++++\n rerere.c         |  5 +++--\n 6 files changed, 47 insertions(+), 36 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 1cd802dd92a..cd9371536f8 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -267,7 +267,8 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,\n {\n \tFILE *interactive;\n \n-\tif (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))\n+\tif (!is_directory(merge_dir()) &&\n+\t    safe_create_dir_in_gitdir(the_repository, merge_dir()))\n \t\treturn error_errno(_(\"could not create temporary %s\"), merge_dir());\n \n \trefs_delete_reflog(get_main_ref_store(the_repository), \"REBASE_HEAD\");\ndiff --git a/object-file.c b/object-file.c\nindex 772c311f188..00451876bd0 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -90,36 +90,6 @@ static int get_conv_flags(unsigned flags)\n \t\treturn 0;\n }\n \n-\n-int mkdir_in_gitdir(const char *path)\n-{\n-\tif (mkdir(path, 0777)) {\n-\t\tint saved_errno = errno;\n-\t\tstruct stat st;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (errno != EEXIST)\n-\t\t\treturn -1;\n-\t\t/*\n-\t\t * Are we looking at a path in a symlinked worktree\n-\t\t * whose original repository does not yet have it?\n-\t\t * e.g. .git/rr-cache pointing at its original\n-\t\t * repository in which the user hasn't performed any\n-\t\t * conflict resolution yet?\n-\t\t */\n-\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n-\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n-\t\t    !is_absolute_path(sb.buf) ||\n-\t\t    mkdir(sb.buf, 0777)) {\n-\t\t\tstrbuf_release(&sb);\n-\t\t\terrno = saved_errno;\n-\t\t\treturn -1;\n-\t\t}\n-\t\tstrbuf_release(&sb);\n-\t}\n-\treturn adjust_shared_perm(the_repository, path);\n-}\n-\n static enum scld_error safe_create_leading_directories_1(char *path, int share)\n {\n \tchar *next_component = path + offset_1st_component(path);\n@@ -2196,7 +2166,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,\n \t\tstruct strbuf dir = STRBUF_INIT;\n \t\tstrbuf_add(&dir, filename.buf, dirlen);\n \n-\t\tif (mkdir_in_gitdir(dir.buf) && errno != EEXIST) {\n+\t\tif (safe_create_dir_in_gitdir(the_repository, dir.buf) &&\n+\t\t    errno != EEXIST) {\n \t\t\terr = error_errno(_(\"unable to create directory %s\"), dir.buf);\n \t\t\tstrbuf_release(&dir);\n \t\t\tgoto cleanup;\ndiff --git a/object-file.h b/object-file.h\nindex 81b30d269c8..4649a3f37d4 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -54,8 +54,6 @@ enum scld_error safe_create_leading_directories(char *path);\n enum scld_error safe_create_leading_directories_const(const char *path);\n enum scld_error safe_create_leading_directories_no_share(char *path);\n \n-int mkdir_in_gitdir(const char *path);\n-\n int git_open_cloexec(const char *name, int flags);\n #define git_open(name) git_open_cloexec(name, O_RDONLY)\n \ndiff --git a/path.c b/path.c\nindex 910756c8b32..c688f874580 100644\n--- a/path.c\n+++ b/path.c\n@@ -902,6 +902,35 @@ void safe_create_dir(struct repository *repo, const char *dir, int share)\n \t\tdie(_(\"Could not make %s writable by group\"), dir);\n }\n \n+int safe_create_dir_in_gitdir(struct repository *repo, const char *path)\n+{\n+\tif (mkdir(path, 0777)) {\n+\t\tint saved_errno = errno;\n+\t\tstruct stat st;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (errno != EEXIST)\n+\t\t\treturn -1;\n+\t\t/*\n+\t\t * Are we looking at a path in a symlinked worktree\n+\t\t * whose original repository does not yet have it?\n+\t\t * e.g. .git/rr-cache pointing at its original\n+\t\t * repository in which the user hasn't performed any\n+\t\t * conflict resolution yet?\n+\t\t */\n+\t\tif (lstat(path, &st) || !S_ISLNK(st.st_mode) ||\n+\t\t    strbuf_readlink(&sb, path, st.st_size) ||\n+\t\t    !is_absolute_path(sb.buf) ||\n+\t\t    mkdir(sb.buf, 0777)) {\n+\t\t\tstrbuf_release(&sb);\n+\t\t\terrno = saved_errno;\n+\t\t\treturn -1;\n+\t\t}\n+\t\tstrbuf_release(&sb);\n+\t}\n+\treturn adjust_shared_perm(repo, path);\n+}\n+\n static int have_same_root(const char *path1, const char *path2)\n {\n \tint is_abs1, is_abs2;\ndiff --git a/path.h b/path.h\nindex 65fe968a13a..a427516d818 100644\n--- a/path.h\n+++ b/path.h\n@@ -221,6 +221,17 @@ char *xdg_cache_home(const char *filename);\n  */\n void safe_create_dir(struct repository *repo, const char *dir, int share);\n \n+/*\n+ * Similar to `safe_create_dir()`, but with two differences:\n+ *\n+ *   - It knows to resolve gitlink files for symlinked worktrees.\n+ *\n+ *   - It always adjusts shared permissions.\n+ *\n+ * Returns a negative erorr code on error, 0 on success.\n+ */\n+int safe_create_dir_in_gitdir(struct repository *repo, const char *path);\n+\n # ifdef USE_THE_REPOSITORY_VARIABLE\n #  include \"strbuf.h\"\n #  include \"repository.h\"\ndiff --git a/rerere.c b/rerere.c\nindex 740e8ad1a0b..0832cc54840 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -860,7 +860,7 @@ static int do_plain_rerere(struct repository *r,\n \t\tstring_list_insert(rr, path)->util = id;\n \n \t\t/* Ensure that the directory exists. */\n-\t\tmkdir_in_gitdir(rerere_path(&buf, id, NULL));\n+\t\tsafe_create_dir_in_gitdir(the_repository, rerere_path(&buf, id, NULL));\n \t}\n \n \tfor (i = 0; i < rr->nr; i++)\n@@ -895,7 +895,8 @@ static int is_rerere_enabled(void)\n \tif (rerere_enabled < 0)\n \t\treturn rr_cache_exists;\n \n-\tif (!rr_cache_exists && mkdir_in_gitdir(git_path_rr_cache()))\n+\tif (!rr_cache_exists &&\n+\t    safe_create_dir_in_gitdir(the_repository, git_path_rr_cache()))\n \t\tdie(_(\"could not create directory '%s'\"), git_path_rr_cache());\n \treturn 1;\n }\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516170","messageId":"20250415-pks-split-object-file-v3-2-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 02/10] object-file: move `safe_create_leading_directories()` into \"path.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:15Z","receivedAt":"2025-04-15T09:38:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `safe_create_leading_directories()` function and its relatives are\nlocated in \"object-file.c\", which is not a good fit as they provide\ngeneric functionality not related to objects at all. Move them into\n\"path.c\", which already hosts `safe_create_dir()` and its relative\n`safe_create_dir_in_gitdir()`.\n\n\"path.c\" is free of `the_repository`, but the moved functions depend on\n`the_repository` to read the \"core.sharedRepository\" config. Adapt the\nfunction signature to accept a repository as argument to fix the issue\nand adjust callers accordingly.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/bugreport.c                |  4 +-\n builtin/clone.c                    |  4 +-\n builtin/credential-cache--daemon.c |  4 +-\n builtin/diagnose.c                 |  4 +-\n builtin/difftool.c                 | 29 ++++++++------\n builtin/fast-import.c              |  2 +-\n builtin/fsck.c                     |  2 +-\n builtin/gc.c                       |  7 ++--\n builtin/init-db.c                  |  3 +-\n builtin/log.c                      |  4 +-\n builtin/mv.c                       |  3 +-\n builtin/sparse-checkout.c          |  5 ++-\n builtin/submodule--helper.c        |  4 +-\n builtin/worktree.c                 |  8 ++--\n commit-graph.c                     |  2 +-\n dir.c                              |  5 +--\n merge-recursive.c                  |  4 +-\n midx-write.c                       |  2 +-\n notes-merge.c                      |  4 +-\n object-file.c                      | 81 +-------------------------------------\n object-file.h                      | 33 ----------------\n path.c                             | 80 +++++++++++++++++++++++++++++++++++++\n path.h                             | 34 ++++++++++++++++\n refs/files-backend.c               |  4 +-\n sequencer.c                        |  4 +-\n server-info.c                      |  2 +-\n submodule.c                        |  2 +-\n 27 files changed, 173 insertions(+), 167 deletions(-)\n\ndiff --git a/builtin/bugreport.c b/builtin/bugreport.c\nindex 66d64bfd5ae..f78c3f2aed6 100644\n--- a/builtin/bugreport.c\n+++ b/builtin/bugreport.c\n@@ -4,13 +4,13 @@\n #include \"editor.h\"\n #include \"gettext.h\"\n #include \"parse-options.h\"\n+#include \"path.h\"\n #include \"strbuf.h\"\n #include \"help.h\"\n #include \"compat/compiler.h\"\n #include \"hook.h\"\n #include \"hook-list.h\"\n #include \"diagnose.h\"\n-#include \"object-file.h\"\n #include \"setup.h\"\n #include \"version.h\"\n \n@@ -141,7 +141,7 @@ int cmd_bugreport(int argc,\n \t}\n \tstrbuf_addstr(&report_path, \".txt\");\n \n-\tswitch (safe_create_leading_directories(report_path.buf)) {\n+\tswitch (safe_create_leading_directories(the_repository, report_path.buf)) {\n \tcase SCLD_OK:\n \tcase SCLD_EXISTS:\n \t\tbreak;\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 2993acb630e..31f2198c1b3 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1090,7 +1090,7 @@ int cmd_clone(int argc,\n \tsigchain_push_common(remove_junk_on_signal);\n \n \tif (!option_bare) {\n-\t\tif (safe_create_leading_directories_const(work_tree) < 0)\n+\t\tif (safe_create_leading_directories_const(the_repository, work_tree) < 0)\n \t\t\tdie_errno(_(\"could not create leading directories of '%s'\"),\n \t\t\t\t  work_tree);\n \t\tif (dest_exists)\n@@ -1111,7 +1111,7 @@ int cmd_clone(int argc,\n \t\t\tjunk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;\n \t\tjunk_git_dir = git_dir;\n \t}\n-\tif (safe_create_leading_directories_const(git_dir) < 0)\n+\tif (safe_create_leading_directories_const(the_repository, git_dir) < 0)\n \t\tdie(_(\"could not create leading directories of '%s'\"), git_dir);\n \n \tif (0 <= option_verbosity) {\ndiff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c\nindex e707618e743..5065ff4660b 100644\n--- a/builtin/credential-cache--daemon.c\n+++ b/builtin/credential-cache--daemon.c\n@@ -2,8 +2,8 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n+#include \"path.h\"\n \n #ifndef NO_UNIX_SOCKETS\n \n@@ -271,7 +271,7 @@ static void init_socket_directory(const char *path)\n \t\t * condition in which somebody can chdir to it, sleep, then try to open\n \t\t * our protected socket.\n \t\t */\n-\t\tif (safe_create_leading_directories_const(dir) < 0)\n+\t\tif (safe_create_leading_directories_const(the_repository, dir) < 0)\n \t\t\tdie_errno(\"unable to create directories for '%s'\", dir);\n \t\tif (mkdir(dir, 0700) < 0)\n \t\t\tdie_errno(\"unable to mkdir '%s'\", dir);\ndiff --git a/builtin/diagnose.c b/builtin/diagnose.c\nindex 33c39bd5981..ec86d66389e 100644\n--- a/builtin/diagnose.c\n+++ b/builtin/diagnose.c\n@@ -3,8 +3,8 @@\n #include \"builtin.h\"\n #include \"abspath.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n+#include \"path.h\"\n #include \"diagnose.h\"\n \n static const char * const diagnose_usage[] = {\n@@ -50,7 +50,7 @@ int cmd_diagnose(int argc,\n \tstrbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);\n \tstrbuf_addstr(&zip_path, \".zip\");\n \n-\tswitch (safe_create_leading_directories(zip_path.buf)) {\n+\tswitch (safe_create_leading_directories(the_repository, zip_path.buf)) {\n \tcase SCLD_OK:\n \tcase SCLD_EXISTS:\n \t\tbreak;\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 41cd00066cc..8292aedaaf0 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -22,6 +22,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"parse-options.h\"\n+#include \"path.h\"\n #include \"read-cache-ll.h\"\n #include \"repository.h\"\n #include \"sparse-index.h\"\n@@ -271,9 +272,9 @@ static void changed_files(struct repository *repo,\n \tstrbuf_release(&buf);\n }\n \n-static int ensure_leading_directories(char *path)\n+static int ensure_leading_directories(struct repository *repo, char *path)\n {\n-\tswitch (safe_create_leading_directories(path)) {\n+\tswitch (safe_create_leading_directories(repo, path)) {\n \t\tcase SCLD_OK:\n \t\tcase SCLD_EXISTS:\n \t\t\treturn 0;\n@@ -341,11 +342,12 @@ static int checkout_path(unsigned mode, struct object_id *oid,\n \treturn ret;\n }\n \n-static void write_file_in_directory(struct strbuf *dir, size_t dir_len,\n-\t\t\tconst char *path, const char *content)\n+static void write_file_in_directory(struct repository *repo,\n+\t\t\t\t    struct strbuf *dir, size_t dir_len,\n+\t\t\t\t    const char *path, const char *content)\n {\n \tadd_path(dir, dir_len, path);\n-\tensure_leading_directories(dir->buf);\n+\tensure_leading_directories(repo, dir->buf);\n \tunlink(dir->buf);\n \twrite_file(dir->buf, \"%s\", content);\n }\n@@ -356,14 +358,15 @@ static void write_file_in_directory(struct strbuf *dir, size_t dir_len,\n  * as text files, resulting in behavior that is analogous to what \"git diff\"\n  * displays for symlink and submodule diffs.\n  */\n-static void write_standin_files(struct pair_entry *entry,\n-\t\t\tstruct strbuf *ldir, size_t ldir_len,\n-\t\t\tstruct strbuf *rdir, size_t rdir_len)\n+static void write_standin_files(struct repository *repo,\n+\t\t\t\tstruct pair_entry *entry,\n+\t\t\t\tstruct strbuf *ldir, size_t ldir_len,\n+\t\t\t\tstruct strbuf *rdir, size_t rdir_len)\n {\n \tif (*entry->left)\n-\t\twrite_file_in_directory(ldir, ldir_len, entry->path, entry->left);\n+\t\twrite_file_in_directory(repo, ldir, ldir_len, entry->path, entry->left);\n \tif (*entry->right)\n-\t\twrite_file_in_directory(rdir, rdir_len, entry->path, entry->right);\n+\t\twrite_file_in_directory(repo, rdir, rdir_len, entry->path, entry->right);\n }\n \n static int run_dir_diff(struct repository *repo,\n@@ -533,7 +536,7 @@ static int run_dir_diff(struct repository *repo,\n \t\t\t\t\t\tADD_CACHE_JUST_APPEND);\n \n \t\t\t\tadd_path(&rdir, rdir_len, dst_path);\n-\t\t\t\tif (ensure_leading_directories(rdir.buf)) {\n+\t\t\t\tif (ensure_leading_directories(repo, rdir.buf)) {\n \t\t\t\t\tret = error(\"could not create \"\n \t\t\t\t\t\t    \"directory for '%s'\",\n \t\t\t\t\t\t    dst_path);\n@@ -576,7 +579,7 @@ static int run_dir_diff(struct repository *repo,\n \t */\n \thashmap_for_each_entry(&submodules, &iter, entry,\n \t\t\t\tentry /* member name */) {\n-\t\twrite_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);\n+\t\twrite_standin_files(repo, entry, &ldir, ldir_len, &rdir, rdir_len);\n \t}\n \n \t/*\n@@ -587,7 +590,7 @@ static int run_dir_diff(struct repository *repo,\n \thashmap_for_each_entry(&symlinks2, &iter, entry,\n \t\t\t\tentry /* member name */) {\n \n-\t\twrite_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);\n+\t\twrite_standin_files(repo, entry, &ldir, ldir_len, &rdir, rdir_len);\n \t}\n \n \tstrbuf_setlen(&ldir, ldir_len);\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 63880b595cc..e4523cc6f1b 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1720,7 +1720,7 @@ static void dump_marks(void)\n \tif (!export_marks_file || (import_marks_file && !import_marks_file_done))\n \t\treturn;\n \n-\tif (safe_create_leading_directories_const(export_marks_file)) {\n+\tif (safe_create_leading_directories_const(the_repository, export_marks_file)) {\n \t\tfailure |= error_errno(\"unable to create leading directories of %s\",\n \t\t\t\t       export_marks_file);\n \t\treturn;\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 9c8a6d6a8df..92312b59591 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -332,7 +332,7 @@ static void check_unreachable_object(struct object *obj)\n \t\t\t\tdescribe_object(&obj->oid));\n \t\t\tFILE *f;\n \n-\t\t\tif (safe_create_leading_directories_const(filename)) {\n+\t\t\tif (safe_create_leading_directories_const(the_repository, filename)) {\n \t\t\t\terror(_(\"could not create lost-found\"));\n \t\t\t\tfree(filename);\n \t\t\t\treturn;\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 99431fd4674..dae1e545514 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -28,7 +28,6 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\n@@ -2099,7 +2098,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit\n \t}\n \tstrbuf_addstr(&plist, \"</array>\\n</dict>\\n</plist>\\n\");\n \n-\tif (safe_create_leading_directories(filename))\n+\tif (safe_create_leading_directories(the_repository, filename))\n \t\tdie(_(\"failed to create directories for '%s'\"), filename);\n \n \tif ((long)lock_file_timeout_ms < 0 &&\n@@ -2565,7 +2564,7 @@ static int systemd_timer_write_timer_file(enum schedule_priority schedule,\n \n \tfilename = xdg_config_home_systemd(local_timer_name);\n \n-\tif (safe_create_leading_directories(filename)) {\n+\tif (safe_create_leading_directories(the_repository, filename)) {\n \t\terror(_(\"failed to create directories for '%s'\"), filename);\n \t\tgoto error;\n \t}\n@@ -2638,7 +2637,7 @@ static int systemd_timer_write_service_template(const char *exec_path)\n \tchar *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, \"\", \"service\");\n \n \tfilename = xdg_config_home_systemd(local_service_name);\n-\tif (safe_create_leading_directories(filename)) {\n+\tif (safe_create_leading_directories(the_repository, filename)) {\n \t\terror(_(\"failed to create directories for '%s'\"), filename);\n \t\tgoto error;\n \t}\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 196dccdd77a..91c2563e341 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -8,7 +8,6 @@\n #include \"abspath.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n-#include \"object-file.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"refs.h\"\n@@ -134,7 +133,7 @@ int cmd_init_db(int argc,\n \t\t\t\t */\n \t\t\t\tsaved = repo_settings_get_shared_repository(the_repository);\n \t\t\t\trepo_settings_set_shared_repository(the_repository, 0);\n-\t\t\t\tswitch (safe_create_leading_directories_const(argv[0])) {\n+\t\t\t\tswitch (safe_create_leading_directories_const(the_repository, argv[0])) {\n \t\t\t\tcase SCLD_OK:\n \t\t\t\tcase SCLD_PERMS:\n \t\t\t\t\tbreak;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 0d4c579dad7..516c9ec8400 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -14,7 +14,6 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n-#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"pager.h\"\n@@ -29,6 +28,7 @@\n #include \"tag.h\"\n #include \"reflog-walk.h\"\n #include \"patch-ids.h\"\n+#include \"path.h\"\n #include \"shortlog.h\"\n #include \"remote.h\"\n #include \"string-list.h\"\n@@ -2311,7 +2311,7 @@ int cmd_format_patch(int argc,\n \t\t */\n \t\tsaved = repo_settings_get_shared_repository(the_repository);\n \t\trepo_settings_set_shared_repository(the_repository, 0);\n-\t\tswitch (safe_create_leading_directories_const(output_directory)) {\n+\t\tswitch (safe_create_leading_directories_const(the_repository, output_directory)) {\n \t\tcase SCLD_OK:\n \t\tcase SCLD_EXISTS:\n \t\t\tbreak;\ndiff --git a/builtin/mv.c b/builtin/mv.c\nindex 55a7d471dca..99fe7a0c561 100644\n--- a/builtin/mv.c\n+++ b/builtin/mv.c\n@@ -15,6 +15,7 @@\n #include \"gettext.h\"\n #include \"name-hash.h\"\n #include \"object-file.h\"\n+#include \"path.h\"\n #include \"pathspec.h\"\n #include \"lockfile.h\"\n #include \"dir.h\"\n@@ -555,7 +556,7 @@ int cmd_mv(int argc,\n \t\t\t\t\t */\n \t\t\t\t\tchar *dst_dup = xstrdup(dst);\n \t\t\t\t\tstring_list_append(&dirty_paths, dst);\n-\t\t\t\t\tsafe_create_leading_directories(dst_dup);\n+\t\t\t\t\tsafe_create_leading_directories(the_repository, dst_dup);\n \t\t\t\t\tFREE_AND_NULL(dst_dup);\n \t\t\t\t\trename(src, dst);\n \t\t\t\t}\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 14dcace5f8f..1bf01591b27 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -9,6 +9,7 @@\n #include \"object-file.h\"\n #include \"object-name.h\"\n #include \"parse-options.h\"\n+#include \"path.h\"\n #include \"pathspec.h\"\n #include \"strbuf.h\"\n #include \"string-list.h\"\n@@ -335,7 +336,7 @@ static int write_patterns_and_update(struct pattern_list *pl)\n \n \tsparse_filename = get_sparse_checkout_filename();\n \n-\tif (safe_create_leading_directories(sparse_filename))\n+\tif (safe_create_leading_directories(the_repository, sparse_filename))\n \t\tdie(_(\"failed to create directory for sparse-checkout file\"));\n \n \thold_lock_file_for_update(&lk, sparse_filename, LOCK_DIE_ON_ERROR);\n@@ -491,7 +492,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,\n \t\tFILE *fp;\n \n \t\t/* assume we are in a fresh repo, but update the sparse-checkout file */\n-\t\tif (safe_create_leading_directories(sparse_filename))\n+\t\tif (safe_create_leading_directories(the_repository, sparse_filename))\n \t\t\tdie(_(\"unable to create leading directories of %s\"),\n \t\t\t    sparse_filename);\n \t\tfp = xfopen(sparse_filename, \"w\");\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 570226ea166..cc001d0b4cd 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1739,7 +1739,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \t\t    !is_empty_dir(clone_data_path))\n \t\t\tdie(_(\"directory not empty: '%s'\"), clone_data_path);\n \n-\t\tif (safe_create_leading_directories_const(sm_gitdir) < 0)\n+\t\tif (safe_create_leading_directories_const(the_repository, sm_gitdir) < 0)\n \t\t\tdie(_(\"could not create directory '%s'\"), sm_gitdir);\n \n \t\tprepare_possible_alternates(clone_data->name, reference);\n@@ -1800,7 +1800,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,\n \t\tif (clone_data->require_init && !stat(clone_data_path, &st) &&\n \t\t    !is_empty_dir(clone_data_path))\n \t\t\tdie(_(\"directory not empty: '%s'\"), clone_data_path);\n-\t\tif (safe_create_leading_directories_const(clone_data_path) < 0)\n+\t\tif (safe_create_leading_directories_const(the_repository, clone_data_path) < 0)\n \t\t\tdie(_(\"could not create directory '%s'\"), clone_data_path);\n \t\tpath = xstrfmt(\"%s/index\", sm_gitdir);\n \t\tunlink_or_warn(path);\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 87ccd47794c..88a36ea9f86 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -348,7 +348,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)\n \tchar *to_file = xstrfmt(\"%s/info/sparse-checkout\", worktree_git_dir);\n \n \tif (file_exists(from_file)) {\n-\t\tif (safe_create_leading_directories(to_file) ||\n+\t\tif (safe_create_leading_directories(the_repository, to_file) ||\n \t\t\tcopy_file(to_file, from_file, 0666))\n \t\t\terror(_(\"failed to copy '%s' to '%s'; sparse-checkout may not work correctly\"),\n \t\t\t\tfrom_file, to_file);\n@@ -367,7 +367,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)\n \t\tstruct config_set cs = { { 0 } };\n \t\tint bare;\n \n-\t\tif (safe_create_leading_directories(to_file) ||\n+\t\tif (safe_create_leading_directories(the_repository, to_file) ||\n \t\t\tcopy_file(to_file, from_file, 0666)) {\n \t\t\terror(_(\"failed to copy worktree config from '%s' to '%s'\"),\n \t\t\t\tfrom_file, to_file);\n@@ -466,7 +466,7 @@ static int add_worktree(const char *path, const char *refname,\n \tname = sb_name.buf;\n \trepo_git_path_replace(the_repository, &sb_repo, \"worktrees/%s\", name);\n \tlen = sb_repo.len;\n-\tif (safe_create_leading_directories_const(sb_repo.buf))\n+\tif (safe_create_leading_directories_const(the_repository, sb_repo.buf))\n \t\tdie_errno(_(\"could not create leading directories of '%s'\"),\n \t\t\t  sb_repo.buf);\n \n@@ -498,7 +498,7 @@ static int add_worktree(const char *path, const char *refname,\n \t\twrite_file(sb.buf, _(\"initializing\"));\n \n \tstrbuf_addf(&sb_git, \"%s/.git\", path);\n-\tif (safe_create_leading_directories_const(sb_git.buf))\n+\tif (safe_create_leading_directories_const(the_repository, sb_git.buf))\n \t\tdie_errno(_(\"could not create leading directories of '%s'\"),\n \t\t\t  sb_git.buf);\n \tjunk_work_tree = xstrdup(path);\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 8286d5dda24..3b5bae00af9 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -2065,7 +2065,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)\n \t\tctx->graph_name = get_commit_graph_filename(ctx->odb);\n \t}\n \n-\tif (safe_create_leading_directories(ctx->graph_name)) {\n+\tif (safe_create_leading_directories(the_repository, ctx->graph_name)) {\n \t\terror(_(\"unable to create leading directories of %s\"),\n \t\t\tctx->graph_name);\n \t\treturn -1;\ndiff --git a/dir.c b/dir.c\nindex 28b0e03feb4..49008739b9b 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,6 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\n #include \"refs.h\"\n@@ -4063,12 +4062,12 @@ void connect_work_tree_and_git_dir(const char *work_tree_,\n \n \t/* Prepare .git file */\n \tstrbuf_addf(&gitfile_sb, \"%s/.git\", work_tree_);\n-\tif (safe_create_leading_directories_const(gitfile_sb.buf))\n+\tif (safe_create_leading_directories_const(the_repository, gitfile_sb.buf))\n \t\tdie(_(\"could not create directories for %s\"), gitfile_sb.buf);\n \n \t/* Prepare config file */\n \tstrbuf_addf(&cfg_sb, \"%s/config\", git_dir_);\n-\tif (safe_create_leading_directories_const(cfg_sb.buf))\n+\tif (safe_create_leading_directories_const(the_repository, cfg_sb.buf))\n \t\tdie(_(\"could not create directories for %s\"), cfg_sb.buf);\n \n \tgit_dir = real_pathdup(git_dir_, 1);\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9aedffc546b..f71490517e1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -910,7 +910,7 @@ static int make_room_for_path(struct merge_options *opt, const char *path)\n \t}\n \n \t/* Make sure leading directories are created */\n-\tstatus = safe_create_leading_directories_const(path);\n+\tstatus = safe_create_leading_directories_const(the_repository, path);\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n@@ -1003,7 +1003,7 @@ static int update_file_flags(struct merge_options *opt,\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(contents->mode)) {\n \t\t\tchar *lnk = xmemdupz(buf, size);\n-\t\t\tsafe_create_leading_directories_const(path);\n+\t\t\tsafe_create_leading_directories_const(the_repository, path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n \t\t\t\tret = err(opt, _(\"failed to symlink '%s': %s\"),\ndiff --git a/midx-write.c b/midx-write.c\nindex a628ac24dcb..fbba55f9d92 100644\n--- a/midx-write.c\n+++ b/midx-write.c\n@@ -1086,7 +1086,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,\n \t\t\t    object_dir);\n \telse\n \t\tget_midx_filename(r->hash_algo, &midx_name, object_dir);\n-\tif (safe_create_leading_directories(midx_name.buf))\n+\tif (safe_create_leading_directories(r, midx_name.buf))\n \t\tdie_errno(_(\"unable to create leading directories of %s\"),\n \t\t\t  midx_name.buf);\n \ndiff --git a/notes-merge.c b/notes-merge.c\nindex 5008faef450..fce45043655 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -296,7 +296,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)\n \t\t\t\t    \"(%s exists).\"), repo_git_path_replace(the_repository, &buf, \"NOTES_MERGE_*\"));\n \t\t}\n \n-\t\tif (safe_create_leading_directories_const(repo_git_path_replace(the_repository, &buf,\n+\t\tif (safe_create_leading_directories_const(the_repository, repo_git_path_replace(the_repository, &buf,\n \t\t\t\tNOTES_MERGE_WORKTREE \"/.test\")))\n \t\t\tdie_errno(\"unable to create directory %s\",\n \t\t\t\t  repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));\n@@ -314,7 +314,7 @@ static void write_buf_to_worktree(const struct object_id *obj,\n {\n \tint fd;\n \tchar *path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE \"/%s\", oid_to_hex(obj));\n-\tif (safe_create_leading_directories_const(path))\n+\tif (safe_create_leading_directories_const(the_repository, path))\n \t\tdie_errno(\"unable to create directory for '%s'\", path);\n \n \tfd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);\ndiff --git a/object-file.c b/object-file.c\nindex 00451876bd0..6228e1c40f8 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -90,83 +90,6 @@ static int get_conv_flags(unsigned flags)\n \t\treturn 0;\n }\n \n-static enum scld_error safe_create_leading_directories_1(char *path, int share)\n-{\n-\tchar *next_component = path + offset_1st_component(path);\n-\tenum scld_error ret = SCLD_OK;\n-\n-\twhile (ret == SCLD_OK && next_component) {\n-\t\tstruct stat st;\n-\t\tchar *slash = next_component, slash_character;\n-\n-\t\twhile (*slash && !is_dir_sep(*slash))\n-\t\t\tslash++;\n-\n-\t\tif (!*slash)\n-\t\t\tbreak;\n-\n-\t\tnext_component = slash + 1;\n-\t\twhile (is_dir_sep(*next_component))\n-\t\t\tnext_component++;\n-\t\tif (!*next_component)\n-\t\t\tbreak;\n-\n-\t\tslash_character = *slash;\n-\t\t*slash = '\\0';\n-\t\tif (!stat(path, &st)) {\n-\t\t\t/* path exists */\n-\t\t\tif (!S_ISDIR(st.st_mode)) {\n-\t\t\t\terrno = ENOTDIR;\n-\t\t\t\tret = SCLD_EXISTS;\n-\t\t\t}\n-\t\t} else if (mkdir(path, 0777)) {\n-\t\t\tif (errno == EEXIST &&\n-\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n-\t\t\t\t; /* somebody created it since we checked */\n-\t\t\telse if (errno == ENOENT)\n-\t\t\t\t/*\n-\t\t\t\t * Either mkdir() failed because\n-\t\t\t\t * somebody just pruned the containing\n-\t\t\t\t * directory, or stat() failed because\n-\t\t\t\t * the file that was in our way was\n-\t\t\t\t * just removed.  Either way, inform\n-\t\t\t\t * the caller that it might be worth\n-\t\t\t\t * trying again:\n-\t\t\t\t */\n-\t\t\t\tret = SCLD_VANISHED;\n-\t\t\telse\n-\t\t\t\tret = SCLD_FAILED;\n-\t\t} else if (share && adjust_shared_perm(the_repository, path)) {\n-\t\t\tret = SCLD_PERMS;\n-\t\t}\n-\t\t*slash = slash_character;\n-\t}\n-\treturn ret;\n-}\n-\n-enum scld_error safe_create_leading_directories(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 1);\n-}\n-\n-enum scld_error safe_create_leading_directories_no_share(char *path)\n-{\n-\treturn safe_create_leading_directories_1(path, 0);\n-}\n-\n-enum scld_error safe_create_leading_directories_const(const char *path)\n-{\n-\tint save_errno;\n-\t/* path points to cache entries, so xstrdup before messing with it */\n-\tchar *buf = xstrdup(path);\n-\tenum scld_error result = safe_create_leading_directories(buf);\n-\n-\tsave_errno = errno;\n-\tfree(buf);\n-\terrno = save_errno;\n-\treturn result;\n-}\n-\n int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n {\n \tint fd;\n@@ -183,7 +106,7 @@ int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n \t/* slow path */\n \t/* some mkstemp implementations erase temp_filename on failure */\n \trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tsafe_create_leading_directories(temp_filename->buf);\n+\tsafe_create_leading_directories(the_repository, temp_filename->buf);\n \treturn xmkstemp_mode(temp_filename->buf, mode);\n }\n \n@@ -196,7 +119,7 @@ int odb_pack_keep(const char *name)\n \t\treturn fd;\n \n \t/* slow path */\n-\tsafe_create_leading_directories_const(name);\n+\tsafe_create_leading_directories_const(the_repository, name);\n \treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n }\n \ndiff --git a/object-file.h b/object-file.h\nindex 4649a3f37d4..922f2bba8c9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,39 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-/*\n- * Create the directory containing the named path, using care to be\n- * somewhat safe against races. Return one of the scld_error values to\n- * indicate success/failure. On error, set errno to describe the\n- * problem.\n- *\n- * SCLD_VANISHED indicates that one of the ancestor directories of the\n- * path existed at one point during the function call and then\n- * suddenly vanished, probably because another process pruned the\n- * directory while we were working.  To be robust against this kind of\n- * race, callers might want to try invoking the function again when it\n- * returns SCLD_VANISHED.\n- *\n- * safe_create_leading_directories() temporarily changes path while it\n- * is working but restores it before returning.\n- * safe_create_leading_directories_const() doesn't modify path, even\n- * temporarily. Both these variants adjust the permissions of the\n- * created directories to honor core.sharedRepository, so they are best\n- * suited for files inside the git dir. For working tree files, use\n- * safe_create_leading_directories_no_share() instead, as it ignores\n- * the core.sharedRepository setting.\n- */\n-enum scld_error {\n-\tSCLD_OK = 0,\n-\tSCLD_FAILED = -1,\n-\tSCLD_PERMS = -2,\n-\tSCLD_EXISTS = -3,\n-\tSCLD_VANISHED = -4\n-};\n-enum scld_error safe_create_leading_directories(char *path);\n-enum scld_error safe_create_leading_directories_const(const char *path);\n-enum scld_error safe_create_leading_directories_no_share(char *path);\n-\n int git_open_cloexec(const char *name, int flags);\n #define git_open(name) git_open_cloexec(name, O_RDONLY)\n \ndiff --git a/path.c b/path.c\nindex c688f874580..62d67166dff 100644\n--- a/path.c\n+++ b/path.c\n@@ -931,6 +931,86 @@ int safe_create_dir_in_gitdir(struct repository *repo, const char *path)\n \treturn adjust_shared_perm(repo, path);\n }\n \n+static enum scld_error safe_create_leading_directories_1(struct repository *repo,\n+\t\t\t\t\t\t\t char *path)\n+{\n+\tchar *next_component = path + offset_1st_component(path);\n+\tenum scld_error ret = SCLD_OK;\n+\n+\twhile (ret == SCLD_OK && next_component) {\n+\t\tstruct stat st;\n+\t\tchar *slash = next_component, slash_character;\n+\n+\t\twhile (*slash && !is_dir_sep(*slash))\n+\t\t\tslash++;\n+\n+\t\tif (!*slash)\n+\t\t\tbreak;\n+\n+\t\tnext_component = slash + 1;\n+\t\twhile (is_dir_sep(*next_component))\n+\t\t\tnext_component++;\n+\t\tif (!*next_component)\n+\t\t\tbreak;\n+\n+\t\tslash_character = *slash;\n+\t\t*slash = '\\0';\n+\t\tif (!stat(path, &st)) {\n+\t\t\t/* path exists */\n+\t\t\tif (!S_ISDIR(st.st_mode)) {\n+\t\t\t\terrno = ENOTDIR;\n+\t\t\t\tret = SCLD_EXISTS;\n+\t\t\t}\n+\t\t} else if (mkdir(path, 0777)) {\n+\t\t\tif (errno == EEXIST &&\n+\t\t\t    !stat(path, &st) && S_ISDIR(st.st_mode))\n+\t\t\t\t; /* somebody created it since we checked */\n+\t\t\telse if (errno == ENOENT)\n+\t\t\t\t/*\n+\t\t\t\t * Either mkdir() failed because\n+\t\t\t\t * somebody just pruned the containing\n+\t\t\t\t * directory, or stat() failed because\n+\t\t\t\t * the file that was in our way was\n+\t\t\t\t * just removed.  Either way, inform\n+\t\t\t\t * the caller that it might be worth\n+\t\t\t\t * trying again:\n+\t\t\t\t */\n+\t\t\t\tret = SCLD_VANISHED;\n+\t\t\telse\n+\t\t\t\tret = SCLD_FAILED;\n+\t\t} else if (repo && adjust_shared_perm(repo, path)) {\n+\t\t\tret = SCLD_PERMS;\n+\t\t}\n+\t\t*slash = slash_character;\n+\t}\n+\treturn ret;\n+}\n+\n+enum scld_error safe_create_leading_directories(struct repository *repo,\n+\t\t\t\t\t\tchar *path)\n+{\n+\treturn safe_create_leading_directories_1(repo, path);\n+}\n+\n+enum scld_error safe_create_leading_directories_no_share(char *path)\n+{\n+\treturn safe_create_leading_directories_1(NULL, path);\n+}\n+\n+enum scld_error safe_create_leading_directories_const(struct repository *repo,\n+\t\t\t\t\t\t      const char *path)\n+{\n+\tint save_errno;\n+\t/* path points to cache entries, so xstrdup before messing with it */\n+\tchar *buf = xstrdup(path);\n+\tenum scld_error result = safe_create_leading_directories(repo, buf);\n+\n+\tsave_errno = errno;\n+\tfree(buf);\n+\terrno = save_errno;\n+\treturn result;\n+}\n+\n static int have_same_root(const char *path1, const char *path2)\n {\n \tint is_abs1, is_abs2;\ndiff --git a/path.h b/path.h\nindex a427516d818..fd1a194b060 100644\n--- a/path.h\n+++ b/path.h\n@@ -232,6 +232,40 @@ void safe_create_dir(struct repository *repo, const char *dir, int share);\n  */\n int safe_create_dir_in_gitdir(struct repository *repo, const char *path);\n \n+/*\n+ * Create the directory containing the named path, using care to be\n+ * somewhat safe against races. Return one of the scld_error values to\n+ * indicate success/failure. On error, set errno to describe the\n+ * problem.\n+ *\n+ * SCLD_VANISHED indicates that one of the ancestor directories of the\n+ * path existed at one point during the function call and then\n+ * suddenly vanished, probably because another process pruned the\n+ * directory while we were working.  To be robust against this kind of\n+ * race, callers might want to try invoking the function again when it\n+ * returns SCLD_VANISHED.\n+ *\n+ * safe_create_leading_directories() temporarily changes path while it\n+ * is working but restores it before returning.\n+ * safe_create_leading_directories_const() doesn't modify path, even\n+ * temporarily. Both these variants adjust the permissions of the\n+ * created directories to honor core.sharedRepository, so they are best\n+ * suited for files inside the git dir. For working tree files, use\n+ * safe_create_leading_directories_no_share() instead, as it ignores\n+ * the core.sharedRepository setting.\n+ */\n+enum scld_error {\n+\tSCLD_OK = 0,\n+\tSCLD_FAILED = -1,\n+\tSCLD_PERMS = -2,\n+\tSCLD_EXISTS = -3,\n+\tSCLD_VANISHED = -4\n+};\n+enum scld_error safe_create_leading_directories(struct repository *repo, char *path);\n+enum scld_error safe_create_leading_directories_const(struct repository *repo,\n+\t\t\t\t\t\t      const char *path);\n+enum scld_error safe_create_leading_directories_no_share(char *path);\n+\n # ifdef USE_THE_REPOSITORY_VARIABLE\n #  include \"strbuf.h\"\n #  include \"repository.h\"\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 91d3aca70a7..10c439a56f8 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -705,7 +705,7 @@ static int lock_raw_ref(struct files_ref_store *refs,\n \tfiles_ref_path(refs, &ref_file, refname);\n \n retry:\n-\tswitch (safe_create_leading_directories(ref_file.buf)) {\n+\tswitch (safe_create_leading_directories(the_repository, ref_file.buf)) {\n \tcase SCLD_OK:\n \t\tbreak; /* success */\n \tcase SCLD_EXISTS:\n@@ -1109,7 +1109,7 @@ static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)\n \t\t\tstrbuf_addstr(&path_copy, path);\n \n \t\tdo {\n-\t\t\tscld_result = safe_create_leading_directories(path_copy.buf);\n+\t\t\tscld_result = safe_create_leading_directories(the_repository, path_copy.buf);\n \t\t\tif (scld_result == SCLD_OK)\n \t\t\t\tgoto retry_fn;\n \t\t} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex c112d2e1c43..9fda9be9266 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4411,7 +4411,7 @@ static int write_update_refs_state(struct string_list *refs_to_oids)\n \t\tgoto cleanup;\n \t}\n \n-\tif (safe_create_leading_directories(path)) {\n+\tif (safe_create_leading_directories(the_repository, path)) {\n \t\tresult = error(_(\"unable to create leading directories of %s\"),\n \t\t\t       path);\n \t\tgoto cleanup;\n@@ -4677,7 +4677,7 @@ static void create_autostash_internal(struct repository *r,\n \t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n \n \t\tif (path) {\n-\t\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tif (safe_create_leading_directories_const(the_repository, path))\n \t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n \t\t\t\t    path);\n \t\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\ndiff --git a/server-info.c b/server-info.c\nindex 1ca0e00d51e..f0646ac92a9 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -88,7 +88,7 @@ static int update_info_file(struct repository *r, char *path,\n \t\t.old_sb = STRBUF_INIT\n \t};\n \n-\tsafe_create_leading_directories(path);\n+\tsafe_create_leading_directories(r, path);\n \tf = mks_tempfile_m(tmp, 0666);\n \tif (!f)\n \t\tgoto out;\ndiff --git a/submodule.c b/submodule.c\nindex 0821507ecaa..218c8c17603 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -2384,7 +2384,7 @@ static void relocate_single_git_dir_into_superproject(const char *path,\n \tif (validate_submodule_git_dir(new_gitdir.buf, sub->name) < 0)\n \t\tdie(_(\"refusing to move '%s' into an existing git dir\"),\n \t\t    real_old_git_dir);\n-\tif (safe_create_leading_directories_const(new_gitdir.buf) < 0)\n+\tif (safe_create_leading_directories_const(the_repository, new_gitdir.buf) < 0)\n \t\tdie(_(\"could not create directory '%s'\"), new_gitdir.buf);\n \treal_new_git_dir = real_pathdup(new_gitdir.buf, 1);\n \n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516172","messageId":"20250415-pks-split-object-file-v3-3-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 03/10] object-file: move `git_open_cloexec()` to \"compat/open.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:16Z","receivedAt":"2025-04-15T09:38:24Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `git_open_cloexec()` wrapper function provides the ability to open a\nfile with `O_CLOEXEC` in a platform-agnostic way. This function is\nprovided by \"object-file.c\" even though it is not specific to the object\nsubsystem at all.\n\nMove the file into \"compat/open.c\". This file already exists before this\ncommit, but has only been compiled conditionally depending on whether or\nnot open(3p) may return EINTR. With this change we now unconditionally\ncompile the object, but wrap `git_open_with_retry()` in an ifdef.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile          |  2 +-\n commit-graph.c    |  1 -\n compat/open.c     | 29 +++++++++++++++++++++++++++++\n git-compat-util.h |  3 +++\n meson.build       |  1 +\n midx.c            |  1 -\n object-file.c     | 27 ---------------------------\n object-file.h     |  3 ---\n pack-bitmap.c     |  1 -\n pack-mtimes.c     |  1 -\n pack-revindex.c   |  1 -\n 11 files changed, 34 insertions(+), 36 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex c41fc41ef0e..bb5407b4703 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -994,6 +994,7 @@ LIB_OBJS += common-exit.o\n LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n+LIB_OBJS += compat/open.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += compiler-tricks/not-constant.o\n LIB_OBJS += config.o\n@@ -1812,7 +1813,6 @@ ifdef FREAD_READS_DIRECTORIES\n endif\n ifdef OPEN_RETURNS_EINTR\n \tCOMPAT_CFLAGS += -DOPEN_RETURNS_EINTR\n-\tCOMPAT_OBJS += compat/open.o\n endif\n ifdef NO_SYMLINK_HEAD\n \tBASIC_CFLAGS += -DNO_SYMLINK_HEAD\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 3b5bae00af9..9621c454972 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -13,7 +13,6 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\ndiff --git a/compat/open.c b/compat/open.c\nindex eb3754a23b8..37ae2b1aeb9 100644\n--- a/compat/open.c\n+++ b/compat/open.c\n@@ -1,5 +1,6 @@\n #include \"git-compat-util.h\"\n \n+#ifdef OPEN_RETURNS_EINTR\n #undef open\n int git_open_with_retry(const char *path, int flags, ...)\n {\n@@ -23,3 +24,31 @@ int git_open_with_retry(const char *path, int flags, ...)\n \n \treturn ret;\n }\n+#endif\n+\n+int git_open_cloexec(const char *name, int flags)\n+{\n+\tint fd;\n+\tstatic int o_cloexec = O_CLOEXEC;\n+\n+\tfd = open(name, flags | o_cloexec);\n+\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n+\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n+\t\to_cloexec &= ~O_CLOEXEC;\n+\t\tfd = open(name, flags | o_cloexec);\n+\t}\n+\n+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n+\t{\n+\t\tstatic int fd_cloexec = FD_CLOEXEC;\n+\n+\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n+\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n+\t\t\tint flags = fcntl(fd, F_GETFD);\n+\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n+\t\t\t\tfd_cloexec = 0;\n+\t\t}\n+\t}\n+#endif\n+\treturn fd;\n+}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex cf733b38acd..9273a8ee087 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1000,6 +1000,9 @@ int git_vsnprintf(char *str, size_t maxsize,\n int git_open_with_retry(const char *path, int flag, ...);\n #endif\n \n+int git_open_cloexec(const char *name, int flags);\n+#define git_open(name) git_open_cloexec(name, O_RDONLY)\n+\n #ifdef __GLIBC_PREREQ\n #if __GLIBC_PREREQ(2, 1)\n #define HAVE_STRCHRNUL\ndiff --git a/meson.build b/meson.build\nindex 145d2f7ff9e..a55e800b85b 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -263,6 +263,7 @@ libgit_sources = [\n   'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n+  'compat/open.c',\n   'compat/terminal.c',\n   'compiler-tricks/not-constant.c',\n   'config.c',\ndiff --git a/midx.c b/midx.c\nindex 807fdf72f7b..3d0015f7828 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -5,7 +5,6 @@\n #include \"dir.h\"\n #include \"hex.h\"\n #include \"packfile.h\"\n-#include \"object-file.h\"\n #include \"hash-lookup.h\"\n #include \"midx.h\"\n #include \"progress.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 6228e1c40f8..c3e20417f3f 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -833,33 +833,6 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \treturn !oideq(oid, &real_oid) ? -1 : 0;\n }\n \n-int git_open_cloexec(const char *name, int flags)\n-{\n-\tint fd;\n-\tstatic int o_cloexec = O_CLOEXEC;\n-\n-\tfd = open(name, flags | o_cloexec);\n-\tif ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {\n-\t\t/* Try again w/o O_CLOEXEC: the kernel might not support it */\n-\t\to_cloexec &= ~O_CLOEXEC;\n-\t\tfd = open(name, flags | o_cloexec);\n-\t}\n-\n-#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)\n-\t{\n-\t\tstatic int fd_cloexec = FD_CLOEXEC;\n-\n-\t\tif (!o_cloexec && 0 <= fd && fd_cloexec) {\n-\t\t\t/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */\n-\t\t\tint flags = fcntl(fd, F_GETFD);\n-\t\t\tif (fcntl(fd, F_SETFD, flags | fd_cloexec))\n-\t\t\t\tfd_cloexec = 0;\n-\t\t}\n-\t}\n-#endif\n-\treturn fd;\n-}\n-\n /*\n  * Find \"oid\" as a loose object in the local repository or in an alternate.\n  * Returns 0 on success, negative on failure.\ndiff --git a/object-file.h b/object-file.h\nindex 922f2bba8c9..353d8a85c33 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,9 +21,6 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n-int git_open_cloexec(const char *name, int flags);\n-#define git_open(name) git_open_cloexec(name, O_RDONLY)\n-\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 7fd78c634ef..0dbd7c4ffe1 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,6 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex cdf30b8d2b0..bcea28e521d 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 038e0c96b1c..1ee7b49e206 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,6 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516171","messageId":"20250415-pks-split-object-file-v3-4-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 04/10] object-file: move `xmmap()` into \"wrapper.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:17Z","receivedAt":"2025-04-15T09:38:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `xmmap()` function is provided by \"object-file.c\" even though its\nfunctionality has nothing to do with the object file subsystem. Move it\ninto \"wrapper.c\", whose header already declares those functions.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-file.c | 48 ------------------------------------------------\n wrapper.c     | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 48 insertions(+), 48 deletions(-)\n\ndiff --git a/object-file.c b/object-file.c\nindex c3e20417f3f..a7868201d09 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -718,54 +718,6 @@ int has_loose_object(const struct object_id *oid)\n \treturn check_and_freshen(oid, 0);\n }\n \n-static void mmap_limit_check(size_t length)\n-{\n-\tstatic size_t limit = 0;\n-\tif (!limit) {\n-\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n-\t\tif (!limit)\n-\t\t\tlimit = SIZE_MAX;\n-\t}\n-\tif (length > limit)\n-\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n-\t\t    (uintmax_t)length, (uintmax_t)limit);\n-}\n-\n-void *xmmap_gently(void *start, size_t length,\n-\t\t  int prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret;\n-\n-\tmmap_limit_check(length);\n-\tret = mmap(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED && !length)\n-\t\tret = NULL;\n-\treturn ret;\n-}\n-\n-const char *mmap_os_err(void)\n-{\n-\tstatic const char blank[] = \"\";\n-#if defined(__linux__)\n-\tif (errno == ENOMEM) {\n-\t\t/* this continues an existing error message: */\n-\t\tstatic const char enomem[] =\n-\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n-\t\treturn enomem;\n-\t}\n-#endif /* OS-specific bits */\n-\treturn blank;\n-}\n-\n-void *xmmap(void *start, size_t length,\n-\tint prot, int flags, int fd, off_t offset)\n-{\n-\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n-\tif (ret == MAP_FAILED)\n-\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n-\treturn ret;\n-}\n-\n static int format_object_header_literally(char *str, size_t size,\n \t\t\t\t\t  const char *type, size_t objsize)\n {\ndiff --git a/wrapper.c b/wrapper.c\nindex 8b985931490..3c79778055e 100644\n--- a/wrapper.c\n+++ b/wrapper.c\n@@ -829,3 +829,51 @@ uint32_t git_rand(unsigned flags)\n \n \treturn result;\n }\n+\n+static void mmap_limit_check(size_t length)\n+{\n+\tstatic size_t limit = 0;\n+\tif (!limit) {\n+\t\tlimit = git_env_ulong(\"GIT_MMAP_LIMIT\", 0);\n+\t\tif (!limit)\n+\t\t\tlimit = SIZE_MAX;\n+\t}\n+\tif (length > limit)\n+\t\tdie(_(\"attempting to mmap %\"PRIuMAX\" over limit %\"PRIuMAX),\n+\t\t    (uintmax_t)length, (uintmax_t)limit);\n+}\n+\n+void *xmmap_gently(void *start, size_t length,\n+\t\t  int prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret;\n+\n+\tmmap_limit_check(length);\n+\tret = mmap(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED && !length)\n+\t\tret = NULL;\n+\treturn ret;\n+}\n+\n+const char *mmap_os_err(void)\n+{\n+\tstatic const char blank[] = \"\";\n+#if defined(__linux__)\n+\tif (errno == ENOMEM) {\n+\t\t/* this continues an existing error message: */\n+\t\tstatic const char enomem[] =\n+\", check sys.vm.max_map_count and/or RLIMIT_DATA\";\n+\t\treturn enomem;\n+\t}\n+#endif /* OS-specific bits */\n+\treturn blank;\n+}\n+\n+void *xmmap(void *start, size_t length,\n+\tint prot, int flags, int fd, off_t offset)\n+{\n+\tvoid *ret = xmmap_gently(start, length, prot, flags, fd, offset);\n+\tif (ret == MAP_FAILED)\n+\t\tdie_errno(_(\"mmap failed%s\"), mmap_os_err());\n+\treturn ret;\n+}\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516176","messageId":"20250415-pks-split-object-file-v3-5-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 05/10] object-file: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:18Z","receivedAt":"2025-04-15T09:38:25Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"While we have the \"object-store.h\" header, most of the functionality for\nobject stores is actually hosted in \"object-file.c\". This makes it hard\nto find relevant functions and causes us to mix up concerns.\n\nSplit out functions relating to the object store subsystem into a new\n\"object-store.c\" file.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n Makefile                 |   1 +\n builtin/checkout.c       |   1 +\n builtin/merge-file.c     |   1 +\n builtin/mktree.c         |   1 +\n builtin/notes.c          |   1 +\n builtin/receive-pack.c   |   1 +\n builtin/tag.c            |   1 +\n builtin/unpack-file.c    |   1 +\n builtin/unpack-objects.c |   1 +\n commit.c                 |   1 +\n http-push.c              |   1 +\n match-trees.c            |   3 +-\n merge-ort.c              |   3 +-\n meson.build              |   1 +\n notes-cache.c            |   3 +-\n notes.c                  |   3 +-\n object-file.c            | 990 +----------------------------------------------\n object-file.h            |  68 +++-\n object-store-ll.h        |  60 +--\n object-store.c           | 972 ++++++++++++++++++++++++++++++++++++++++++++++\n 20 files changed, 1074 insertions(+), 1040 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex bb5407b4703..d8ad76fb791 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1086,6 +1086,7 @@ LIB_OBJS += notes.o\n LIB_OBJS += object-file-convert.o\n LIB_OBJS += object-file.o\n LIB_OBJS += object-name.o\n+LIB_OBJS += object-store.o\n LIB_OBJS += object.o\n LIB_OBJS += oid-array.o\n LIB_OBJS += oidmap.o\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e7486cf658..3e68623838a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -18,6 +18,7 @@\n #include \"lockfile.h\"\n #include \"mem-pool.h\"\n #include \"merge-recursive.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 7e315f374b2..2b16b10d2ca 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -5,6 +5,7 @@\n #include \"abspath.h\"\n #include \"diff.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store.h\"\n #include \"config.h\"\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 3c16faa40e3..0644f951161 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -11,6 +11,7 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"parse-options.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n \n static struct treeent {\ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex ff61ec5f2da..0dbc233752d 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -14,6 +14,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex b3e2a9d0c60..ee51bd76f60 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -31,6 +31,7 @@\n #include \"tmp-objdir.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7c173535cb3..536a01ff3ae 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"refs.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n #include \"path.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex fb5fcbc40a8..b19e5cabd03 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -2,6 +2,7 @@\n #include \"builtin.h\"\n #include \"config.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n #include \"object-store-ll.h\"\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 3bbcaf2de9b..4078eab9252 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -8,6 +8,7 @@\n #include \"gettext.h\"\n #include \"git-zlib.h\"\n #include \"hex.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"object.h\"\n #include \"delta.h\"\ndiff --git a/commit.c b/commit.c\nindex 48aeefaad31..fbf4f8e87fd 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -29,6 +29,7 @@\n #include \"tree.h\"\n #include \"hook.h\"\n #include \"parse.h\"\n+#include \"object-file.h\"\n #include \"object-file-convert.h\"\n \n static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);\ndiff --git a/http-push.c b/http-push.c\nindex 1b030d96f48..806eb67cf1b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -19,6 +19,7 @@\n #include \"tree-walk.h\"\n #include \"url.h\"\n #include \"packfile.h\"\n+#include \"object-file.h\"\n #include \"object-store-ll.h\"\n #include \"commit-reach.h\"\n \ndiff --git a/match-trees.c b/match-trees.c\nindex ef14ceb594c..72922d5d64e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -6,7 +6,8 @@\n #include \"strbuf.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n static int score_missing(unsigned mode)\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 1554900d984..5de3e2fc159 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -36,8 +36,9 @@\n #include \"merge-ll.h\"\n #include \"match-trees.h\"\n #include \"mem-pool.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"promisor-remote.h\"\ndiff --git a/meson.build b/meson.build\nindex a55e800b85b..f656f978020 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -355,6 +355,7 @@ libgit_sources = [\n   'object-file-convert.c',\n   'object-file.c',\n   'object-name.c',\n+  'object-store.c',\n   'object.c',\n   'oid-array.c',\n   'oidmap.c',\ndiff --git a/notes-cache.c b/notes-cache.c\nindex ecfdf6e43b5..150241b15e0 100644\n--- a/notes-cache.c\n+++ b/notes-cache.c\n@@ -2,7 +2,8 @@\n \n #include \"git-compat-util.h\"\n #include \"notes-cache.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"repository.h\"\n #include \"commit.h\"\ndiff --git a/notes.c b/notes.c\nindex ce5a1006a83..d9645c4b5dc 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -6,8 +6,9 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"notes.h\"\n+#include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"strbuf.h\"\n #include \"tree-walk.h\"\ndiff --git a/object-file.c b/object-file.c\nindex a7868201d09..baa828822ea 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -11,75 +11,26 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"abspath.h\"\n-#include \"config.h\"\n+#include \"bulk-checkin.h\"\n #include \"convert.h\"\n #include \"environment.h\"\n+#include \"fsck.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"string-list.h\"\n-#include \"lockfile.h\"\n-#include \"pack.h\"\n-#include \"commit.h\"\n-#include \"run-command.h\"\n-#include \"refs.h\"\n-#include \"bulk-checkin.h\"\n-#include \"repository.h\"\n-#include \"replace-object.h\"\n-#include \"streaming.h\"\n-#include \"dir.h\"\n-#include \"list.h\"\n-#include \"quote.h\"\n-#include \"packfile.h\"\n+#include \"loose.h\"\n+#include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n #include \"oidtree.h\"\n+#include \"pack.h\"\n+#include \"packfile.h\"\n #include \"path.h\"\n-#include \"promisor-remote.h\"\n #include \"setup.h\"\n-#include \"submodule.h\"\n-#include \"fsck.h\"\n-#include \"loose.h\"\n-#include \"object-file-convert.h\"\n+#include \"streaming.h\"\n \n /* The maximum size for an object header. */\n #define MAX_HEADER_LEN 32\n \n-/*\n- * This is meant to hold a *small* number of objects that you would\n- * want repo_read_object_file() to be able to return, but yet you do not want\n- * to write them into the object store (e.g. a browse-only\n- * application).\n- */\n-static struct cached_object_entry {\n-\tstruct object_id oid;\n-\tstruct cached_object {\n-\t\tenum object_type type;\n-\t\tconst void *buf;\n-\t\tunsigned long size;\n-\t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n-\n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n-{\n-\tstatic const struct cached_object empty_tree = {\n-\t\t.type = OBJ_TREE,\n-\t\t.buf = \"\",\n-\t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n-\n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n-\t\tif (oideq(&co->oid, oid))\n-\t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n-\t\treturn &empty_tree;\n-\treturn NULL;\n-}\n-\n-\n static int get_conv_flags(unsigned flags)\n {\n \tif (flags & HASH_RENORMALIZE)\n@@ -90,39 +41,6 @@ static int get_conv_flags(unsigned flags)\n \t\treturn 0;\n }\n \n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n-{\n-\tint fd;\n-\t/*\n-\t * we let the umask do its job, don't try to be more\n-\t * restrictive except to remove write permission.\n-\t */\n-\tint mode = 0444;\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\t/* some mkstemp implementations erase temp_filename on failure */\n-\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n-\tsafe_create_leading_directories(the_repository, temp_filename->buf);\n-\treturn xmkstemp_mode(temp_filename->buf, mode);\n-}\n-\n-int odb_pack_keep(const char *name)\n-{\n-\tint fd;\n-\n-\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-\tif (0 <= fd)\n-\t\treturn fd;\n-\n-\t/* slow path */\n-\tsafe_create_leading_directories_const(the_repository, name);\n-\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n-}\n-\n static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n {\n \tint i;\n@@ -136,9 +54,9 @@ static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)\n \t}\n }\n \n-static const char *odb_loose_path(struct object_directory *odb,\n-\t\t\t\t  struct strbuf *buf,\n-\t\t\t\t  const struct object_id *oid)\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid)\n {\n \tstrbuf_reset(buf);\n \tstrbuf_addstr(buf, odb->path);\n@@ -147,513 +65,6 @@ static const char *odb_loose_path(struct object_directory *odb,\n \treturn buf->buf;\n }\n \n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid)\n-{\n-\treturn odb_loose_path(r->objects->odb, buf, oid);\n-}\n-\n-/*\n- * Return non-zero iff the path is usable as an alternate object database.\n- */\n-static int alt_odb_usable(struct raw_object_store *o,\n-\t\t\t  struct strbuf *path,\n-\t\t\t  const char *normalized_objdir, khiter_t *pos)\n-{\n-\tint r;\n-\n-\t/* Detect cases where alternate disappeared */\n-\tif (!is_directory(path->buf)) {\n-\t\terror(_(\"object directory %s does not exist; \"\n-\t\t\t\"check .git/objects/info/alternates\"),\n-\t\t      path->buf);\n-\t\treturn 0;\n-\t}\n-\n-\t/*\n-\t * Prevent the common mistake of listing the same\n-\t * thing twice, or object directory itself.\n-\t */\n-\tif (!o->odb_by_path) {\n-\t\tkhiter_t p;\n-\n-\t\to->odb_by_path = kh_init_odb_path_map();\n-\t\tassert(!o->odb->next);\n-\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n-\t\tassert(r == 1); /* never used */\n-\t\tkh_value(o->odb_by_path, p) = o->odb;\n-\t}\n-\tif (fspatheq(path->buf, normalized_objdir))\n-\t\treturn 0;\n-\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n-\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n-\treturn r == 0 ? 0 : 1;\n-}\n-\n-/*\n- * Prepare alternate object database registry.\n- *\n- * The variable alt_odb_list points at the list of struct\n- * object_directory.  The elements on this list come from\n- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n- * whose contents is similar to that environment variable but can be\n- * LF separated.  Its base points at a statically allocated buffer that\n- * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n- * its name points just after the slash at the end of \".git/objects/\"\n- * in the example above, and has enough space to hold all hex characters\n- * of the object ID, an extra slash for the first level indirection, and\n- * the terminating NUL.\n- */\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth);\n-static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n-\tconst char *relative_base, int depth, const char *normalized_objdir)\n-{\n-\tstruct object_directory *ent;\n-\tstruct strbuf pathbuf = STRBUF_INIT;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\tkhiter_t pos;\n-\tint ret = -1;\n-\n-\tif (!is_absolute_path(entry->buf) && relative_base) {\n-\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n-\t\tstrbuf_addch(&pathbuf, '/');\n-\t}\n-\tstrbuf_addbuf(&pathbuf, entry);\n-\n-\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n-\t\terror(_(\"unable to normalize alternate object path: %s\"),\n-\t\t      pathbuf.buf);\n-\t\tgoto error;\n-\t}\n-\tstrbuf_swap(&pathbuf, &tmp);\n-\n-\t/*\n-\t * The trailing slash after the directory name is given by\n-\t * this function at the end. Remove duplicates.\n-\t */\n-\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n-\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n-\n-\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n-\t\tgoto error;\n-\n-\tCALLOC_ARRAY(ent, 1);\n-\t/* pathbuf.buf is already in r->objects->odb_by_path */\n-\tent->path = strbuf_detach(&pathbuf, NULL);\n-\n-\t/* add the alternate entry */\n-\t*r->objects->odb_tail = ent;\n-\tr->objects->odb_tail = &(ent->next);\n-\tent->next = NULL;\n-\tassert(r->objects->odb_by_path);\n-\tkh_value(r->objects->odb_by_path, pos) = ent;\n-\n-\t/* recursively add alternates */\n-\tread_info_alternates(r, ent->path, depth + 1);\n-\tret = 0;\n- error:\n-\tstrbuf_release(&tmp);\n-\tstrbuf_release(&pathbuf);\n-\treturn ret;\n-}\n-\n-static const char *parse_alt_odb_entry(const char *string,\n-\t\t\t\t       int sep,\n-\t\t\t\t       struct strbuf *out)\n-{\n-\tconst char *end;\n-\n-\tstrbuf_reset(out);\n-\n-\tif (*string == '#') {\n-\t\t/* comment; consume up to next separator */\n-\t\tend = strchrnul(string, sep);\n-\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n-\t\t/*\n-\t\t * quoted path; unquote_c_style has copied the\n-\t\t * data for us and set \"end\". Broken quoting (e.g.,\n-\t\t * an entry that doesn't end with a quote) falls\n-\t\t * back to the unquoted case below.\n-\t\t */\n-\t} else {\n-\t\t/* normal, unquoted path */\n-\t\tend = strchrnul(string, sep);\n-\t\tstrbuf_add(out, string, end - string);\n-\t}\n-\n-\tif (*end)\n-\t\tend++;\n-\treturn end;\n-}\n-\n-static void link_alt_odb_entries(struct repository *r, const char *alt,\n-\t\t\t\t int sep, const char *relative_base, int depth)\n-{\n-\tstruct strbuf objdirbuf = STRBUF_INIT;\n-\tstruct strbuf entry = STRBUF_INIT;\n-\n-\tif (!alt || !*alt)\n-\t\treturn;\n-\n-\tif (depth > 5) {\n-\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n-\t\t\t\trelative_base);\n-\t\treturn;\n-\t}\n-\n-\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n-\n-\twhile (*alt) {\n-\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n-\t\tif (!entry.len)\n-\t\t\tcontinue;\n-\t\tlink_alt_odb_entry(r, &entry,\n-\t\t\t\t   relative_base, depth, objdirbuf.buf);\n-\t}\n-\tstrbuf_release(&entry);\n-\tstrbuf_release(&objdirbuf);\n-}\n-\n-static void read_info_alternates(struct repository *r,\n-\t\t\t\t const char *relative_base,\n-\t\t\t\t int depth)\n-{\n-\tchar *path;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n-\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n-\t\twarn_on_fopen_errors(path);\n-\t\tfree(path);\n-\t\treturn;\n-\t}\n-\n-\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n-\tstrbuf_release(&buf);\n-\tfree(path);\n-}\n-\n-void add_to_alternates_file(const char *reference)\n-{\n-\tstruct lock_file lock = LOCK_INIT;\n-\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n-\tFILE *in, *out;\n-\tint found = 0;\n-\n-\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n-\tout = fdopen_lock_file(&lock, \"w\");\n-\tif (!out)\n-\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n-\n-\tin = fopen(alts, \"r\");\n-\tif (in) {\n-\t\tstruct strbuf line = STRBUF_INIT;\n-\n-\t\twhile (strbuf_getline(&line, in) != EOF) {\n-\t\t\tif (!strcmp(reference, line.buf)) {\n-\t\t\t\tfound = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n-\t\t}\n-\n-\t\tstrbuf_release(&line);\n-\t\tfclose(in);\n-\t}\n-\telse if (errno != ENOENT)\n-\t\tdie_errno(_(\"unable to read alternates file\"));\n-\n-\tif (found) {\n-\t\trollback_lock_file(&lock);\n-\t} else {\n-\t\tfprintf_or_die(out, \"%s\\n\", reference);\n-\t\tif (commit_lock_file(&lock))\n-\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n-\t\tif (the_repository->objects->loaded_alternates)\n-\t\t\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t\t\t     '\\n', NULL, 0);\n-\t}\n-\tfree(alts);\n-}\n-\n-void add_to_alternates_memory(const char *reference)\n-{\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\tlink_alt_odb_entries(the_repository, reference,\n-\t\t\t     '\\n', NULL, 0);\n-}\n-\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n-{\n-\tstruct object_directory *new_odb;\n-\n-\t/*\n-\t * Make sure alternates are initialized, or else our entry may be\n-\t * overwritten when they are.\n-\t */\n-\tprepare_alt_odb(the_repository);\n-\n-\t/*\n-\t * Make a new primary odb and link the old primary ODB in as an\n-\t * alternate\n-\t */\n-\tnew_odb = xcalloc(1, sizeof(*new_odb));\n-\tnew_odb->path = xstrdup(dir);\n-\n-\t/*\n-\t * Disable ref updates while a temporary odb is active, since\n-\t * the objects in the database may roll back.\n-\t */\n-\tnew_odb->disable_ref_updates = 1;\n-\tnew_odb->will_destroy = will_destroy;\n-\tnew_odb->next = the_repository->objects->odb;\n-\tthe_repository->objects->odb = new_odb;\n-\treturn new_odb->next;\n-}\n-\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n-{\n-\tstruct object_directory *cur_odb = the_repository->objects->odb;\n-\n-\tif (strcmp(old_path, cur_odb->path))\n-\t\tBUG(\"expected %s as primary object store; found %s\",\n-\t\t    old_path, cur_odb->path);\n-\n-\tif (cur_odb->next != restore_odb)\n-\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n-\n-\tthe_repository->objects->odb = restore_odb;\n-\tfree_object_directory(cur_odb);\n-}\n-\n-/*\n- * Compute the exact path an alternate is at and returns it. In case of\n- * error NULL is returned and the human readable error is added to `err`\n- * `path` may be relative and should point to $GIT_DIR.\n- * `err` must not be null.\n- */\n-char *compute_alternate_path(const char *path, struct strbuf *err)\n-{\n-\tchar *ref_git = NULL;\n-\tconst char *repo;\n-\tint seen_error = 0;\n-\n-\tref_git = real_pathdup(path, 0);\n-\tif (!ref_git) {\n-\t\tseen_error = 1;\n-\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\trepo = read_gitfile(ref_git);\n-\tif (!repo)\n-\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n-\tif (repo) {\n-\t\tfree(ref_git);\n-\t\tref_git = xstrdup(repo);\n-\t}\n-\n-\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n-\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n-\t\tfree(ref_git);\n-\t\tref_git = ref_git_git;\n-\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tseen_error = 1;\n-\t\tif (get_common_dir(&sb, ref_git)) {\n-\t\t\tstrbuf_addf(err,\n-\t\t\t\t    _(\"reference repository '%s' as a linked \"\n-\t\t\t\t      \"checkout is not supported yet.\"),\n-\t\t\t\t    path);\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n-\t\t\t\t\t\"local repository.\"), path);\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n-\t\tstrbuf_addf(err,\n-\t\t\t    _(\"reference repository '%s' is grafted\"),\n-\t\t\t    path);\n-\t\tseen_error = 1;\n-\t\tgoto out;\n-\t}\n-\n-out:\n-\tif (seen_error) {\n-\t\tFREE_AND_NULL(ref_git);\n-\t}\n-\n-\treturn ref_git;\n-}\n-\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n-{\n-\tstruct object_directory *odb;\n-\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n-\tstruct strbuf odb_path_real = STRBUF_INIT;\n-\n-\tprepare_alt_odb(r);\n-\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n-\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n-\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n-\t\t\tbreak;\n-\t}\n-\n-\tfree(obj_dir_real);\n-\tstrbuf_release(&odb_path_real);\n-\n-\tif (!odb)\n-\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n-\treturn odb;\n-}\n-\n-static void fill_alternate_refs_command(struct child_process *cmd,\n-\t\t\t\t\tconst char *repo_path)\n-{\n-\tconst char *value;\n-\n-\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n-\t\tcmd->use_shell = 1;\n-\n-\t\tstrvec_push(&cmd->args, value);\n-\t\tstrvec_push(&cmd->args, repo_path);\n-\t} else {\n-\t\tcmd->git_cmd = 1;\n-\n-\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n-\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n-\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n-\n-\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n-\t\t\tstrvec_push(&cmd->args, \"--\");\n-\t\t\tstrvec_split(&cmd->args, value);\n-\t\t}\n-\t}\n-\n-\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n-\tcmd->out = -1;\n-}\n-\n-static void read_alternate_refs(const char *path,\n-\t\t\t\talternate_ref_fn *cb,\n-\t\t\t\tvoid *data)\n-{\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tstruct strbuf line = STRBUF_INIT;\n-\tFILE *fh;\n-\n-\tfill_alternate_refs_command(&cmd, path);\n-\n-\tif (start_command(&cmd))\n-\t\treturn;\n-\n-\tfh = xfdopen(cmd.out, \"r\");\n-\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n-\t\tstruct object_id oid;\n-\t\tconst char *p;\n-\n-\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n-\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n-\t\t\t\tline.buf);\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\tcb(&oid, data);\n-\t}\n-\n-\tfclose(fh);\n-\tfinish_command(&cmd);\n-\tstrbuf_release(&line);\n-}\n-\n-struct alternate_refs_data {\n-\talternate_ref_fn *fn;\n-\tvoid *data;\n-};\n-\n-static int refs_from_alternate_cb(struct object_directory *e,\n-\t\t\t\t  void *data)\n-{\n-\tstruct strbuf path = STRBUF_INIT;\n-\tsize_t base_len;\n-\tstruct alternate_refs_data *cb = data;\n-\n-\tif (!strbuf_realpath(&path, e->path, 0))\n-\t\tgoto out;\n-\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n-\t\tgoto out;\n-\tbase_len = path.len;\n-\n-\t/* Is this a git repository with refs? */\n-\tstrbuf_addstr(&path, \"/refs\");\n-\tif (!is_directory(path.buf))\n-\t\tgoto out;\n-\tstrbuf_setlen(&path, base_len);\n-\n-\tread_alternate_refs(path.buf, cb->fn, cb->data);\n-\n-out:\n-\tstrbuf_release(&path);\n-\treturn 0;\n-}\n-\n-void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n-{\n-\tstruct alternate_refs_data cb;\n-\tcb.fn = fn;\n-\tcb.data = data;\n-\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n-}\n-\n-int foreach_alt_odb(alt_odb_fn fn, void *cb)\n-{\n-\tstruct object_directory *ent;\n-\tint r = 0;\n-\n-\tprepare_alt_odb(the_repository);\n-\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n-\t\tr = fn(ent, cb);\n-\t\tif (r)\n-\t\t\tbreak;\n-\t}\n-\treturn r;\n-}\n-\n-void prepare_alt_odb(struct repository *r)\n-{\n-\tif (r->objects->loaded_alternates)\n-\t\treturn;\n-\n-\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n-\n-\tread_info_alternates(r, r->objects->odb->path, 0);\n-\tr->objects->loaded_alternates = 1;\n-}\n-\n-int has_alt_odb(struct repository *r)\n-{\n-\tprepare_alt_odb(r);\n-\treturn !!r->objects->odb->next;\n-}\n-\n /* Returns 1 if we have successfully freshened the file, 0 otherwise. */\n static int freshen_file(const char *fn)\n {\n@@ -1055,9 +466,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)\n \treturn 0;\n }\n \n-static int loose_object_info(struct repository *r,\n-\t\t\t     const struct object_id *oid,\n-\t\t\t     struct object_info *oi, int flags)\n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags)\n {\n \tint status = 0;\n \tint fd;\n@@ -1153,345 +564,6 @@ static int loose_object_info(struct repository *r,\n \treturn status;\n }\n \n-int obj_read_use_lock = 0;\n-pthread_mutex_t obj_read_mutex;\n-\n-void enable_obj_read_lock(void)\n-{\n-\tif (obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 1;\n-\tinit_recursive_mutex(&obj_read_mutex);\n-}\n-\n-void disable_obj_read_lock(void)\n-{\n-\tif (!obj_read_use_lock)\n-\t\treturn;\n-\n-\tobj_read_use_lock = 0;\n-\tpthread_mutex_destroy(&obj_read_mutex);\n-}\n-\n-int fetch_if_missing = 1;\n-\n-static int do_oid_object_info_extended(struct repository *r,\n-\t\t\t\t       const struct object_id *oid,\n-\t\t\t\t       struct object_info *oi, unsigned flags)\n-{\n-\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n-\tconst struct cached_object *co;\n-\tstruct pack_entry e;\n-\tint rtype;\n-\tconst struct object_id *real = oid;\n-\tint already_retried = 0;\n-\n-\n-\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n-\t\treal = lookup_replace_object(r, oid);\n-\n-\tif (is_null_oid(real))\n-\t\treturn -1;\n-\n-\tif (!oi)\n-\t\toi = &blank_oi;\n-\n-\tco = find_cached_object(real);\n-\tif (co) {\n-\t\tif (oi->typep)\n-\t\t\t*(oi->typep) = co->type;\n-\t\tif (oi->sizep)\n-\t\t\t*(oi->sizep) = co->size;\n-\t\tif (oi->disk_sizep)\n-\t\t\t*(oi->disk_sizep) = 0;\n-\t\tif (oi->delta_base_oid)\n-\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n-\t\tif (oi->type_name)\n-\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n-\t\tif (oi->contentp)\n-\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n-\t\toi->whence = OI_CACHED;\n-\t\treturn 0;\n-\t}\n-\n-\twhile (1) {\n-\t\tif (find_pack_entry(r, real, &e))\n-\t\t\tbreak;\n-\n-\t\t/* Most likely it's a loose object. */\n-\t\tif (!loose_object_info(r, real, oi, flags))\n-\t\t\treturn 0;\n-\n-\t\t/* Not a loose object; someone else may have just packed it. */\n-\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n-\t\t\treprepare_packed_git(r);\n-\t\t\tif (find_pack_entry(r, real, &e))\n-\t\t\t\tbreak;\n-\t\t}\n-\n-\t\t/*\n-\t\t * If r is the_repository, this might be an attempt at\n-\t\t * accessing a submodule object as if it were in the_repository\n-\t\t * (having called add_submodule_odb() on that submodule's ODB).\n-\t\t * If any such ODBs exist, register them and try again.\n-\t\t */\n-\t\tif (r == the_repository &&\n-\t\t    register_all_submodule_odb_as_alternates())\n-\t\t\t/* We added some alternates; retry */\n-\t\t\tcontinue;\n-\n-\t\t/* Check if it is a missing object */\n-\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n-\t\t    !already_retried &&\n-\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n-\t\t\tpromisor_remote_get_direct(r, real, 1);\n-\t\t\talready_retried = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n-\t\t\tconst struct packed_git *p;\n-\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n-\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n-\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n-\t\t\tif ((p = has_packed_and_bad(r, real)))\n-\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n-\t\t\t\t    oid_to_hex(real), p->pack_name);\n-\t\t}\n-\t\treturn -1;\n-\t}\n-\n-\tif (oi == &blank_oi)\n-\t\t/*\n-\t\t * We know that the caller doesn't actually need the\n-\t\t * information below, so return early.\n-\t\t */\n-\t\treturn 0;\n-\trtype = packed_object_info(r, e.p, e.offset, oi);\n-\tif (rtype < 0) {\n-\t\tmark_bad_packed_object(e.p, real);\n-\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n-\t} else if (oi->whence == OI_PACKED) {\n-\t\toi->u.packed.offset = e.offset;\n-\t\toi->u.packed.pack = e.p;\n-\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n-\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int oid_object_info_convert(struct repository *r,\n-\t\t\t\t   const struct object_id *input_oid,\n-\t\t\t\t   struct object_info *input_oi, unsigned flags)\n-{\n-\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n-\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n-\tstruct strbuf type_name = STRBUF_INIT;\n-\tstruct object_id oid, delta_base_oid;\n-\tstruct object_info new_oi, *oi;\n-\tunsigned long size;\n-\tvoid *content;\n-\tint ret;\n-\n-\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n-\t\tif (do_die)\n-\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n-\t\treturn -1;\n-\t}\n-\n-\t/* Is new_oi needed? */\n-\toi = input_oi;\n-\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n-\t\t\t input_oi->contentp)) {\n-\t\tnew_oi = *input_oi;\n-\t\t/* Does delta_base_oid need to be converted? */\n-\t\tif (input_oi->delta_base_oid)\n-\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n-\t\t/* Will the attributes differ when converted? */\n-\t\tif (input_oi->sizep || input_oi->contentp) {\n-\t\t\tnew_oi.contentp = &content;\n-\t\t\tnew_oi.sizep = &size;\n-\t\t\tnew_oi.type_name = &type_name;\n-\t\t}\n-\t\toi = &new_oi;\n-\t}\n-\n-\tret = oid_object_info_extended(r, &oid, oi, flags);\n-\tif (ret)\n-\t\treturn -1;\n-\tif (oi == input_oi)\n-\t\treturn ret;\n-\n-\tif (new_oi.contentp) {\n-\t\tstruct strbuf outbuf = STRBUF_INIT;\n-\t\tenum object_type type;\n-\n-\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n-\t\t\t\t\t       !do_die);\n-\t\tif (type == -1)\n-\t\t\treturn -1;\n-\t\tif (type != OBJ_BLOB) {\n-\t\t\tret = convert_object_file(the_repository, &outbuf,\n-\t\t\t\t\t\t  the_hash_algo, input_algo,\n-\t\t\t\t\t\t  content, size, type, !do_die);\n-\t\t\tfree(content);\n-\t\t\tif (ret == -1)\n-\t\t\t\treturn -1;\n-\t\t\tsize = outbuf.len;\n-\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n-\t\t}\n-\t\tif (input_oi->sizep)\n-\t\t\t*input_oi->sizep = size;\n-\t\tif (input_oi->contentp)\n-\t\t\t*input_oi->contentp = content;\n-\t\telse\n-\t\t\tfree(content);\n-\t\tif (input_oi->type_name)\n-\t\t\t*input_oi->type_name = type_name;\n-\t\telse\n-\t\t\tstrbuf_release(&type_name);\n-\t}\n-\tif (new_oi.delta_base_oid == &delta_base_oid) {\n-\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n-\t\t\t\t input_oi->delta_base_oid)) {\n-\t\t\tif (do_die)\n-\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n-\t\t\t\t    oid_to_hex(&delta_base_oid),\n-\t\t\t\t    input_algo->name);\n-\t\t\treturn -1;\n-\t\t}\n-\t}\n-\tinput_oi->whence = new_oi.whence;\n-\tinput_oi->u = new_oi.u;\n-\treturn ret;\n-}\n-\n-int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n-\t\t\t     struct object_info *oi, unsigned flags)\n-{\n-\tint ret;\n-\n-\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n-\t\treturn oid_object_info_convert(r, oid, oi, flags);\n-\n-\tobj_read_lock();\n-\tret = do_oid_object_info_extended(r, oid, oi, flags);\n-\tobj_read_unlock();\n-\treturn ret;\n-}\n-\n-\n-/* returns enum object_type or negative */\n-int oid_object_info(struct repository *r,\n-\t\t    const struct object_id *oid,\n-\t\t    unsigned long *sizep)\n-{\n-\tenum object_type type;\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\n-\toi.typep = &type;\n-\toi.sizep = sizep;\n-\tif (oid_object_info_extended(r, oid, &oi,\n-\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n-\t\treturn -1;\n-\treturn type;\n-}\n-\n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid)\n-{\n-\tstruct cached_object_entry *co;\n-\tchar *co_buf;\n-\n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n-\t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n-\tco->value.size = len;\n-\tco->value.type = type;\n-\tco_buf = xmalloc(len);\n-\tmemcpy(co_buf, buf, len);\n-\tco->value.buf = co_buf;\n-\toidcpy(&co->oid, oid);\n-\treturn 0;\n-}\n-\n-/*\n- * This function dies on corrupt objects; the callers who want to\n- * deal with them should arrange to call oid_object_info_extended() and give\n- * error messages themselves.\n- */\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size)\n-{\n-\tstruct object_info oi = OBJECT_INFO_INIT;\n-\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n-\tvoid *data;\n-\n-\toi.typep = type;\n-\toi.sizep = size;\n-\toi.contentp = &data;\n-\tif (oid_object_info_extended(r, oid, &oi, flags))\n-\t\treturn NULL;\n-\n-\treturn data;\n-}\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *actual_oid_return)\n-{\n-\tenum object_type type;\n-\tvoid *buffer;\n-\tunsigned long isize;\n-\tstruct object_id actual_oid;\n-\n-\toidcpy(&actual_oid, oid);\n-\twhile (1) {\n-\t\tint ref_length = -1;\n-\t\tconst char *ref_type = NULL;\n-\n-\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n-\t\tif (!buffer)\n-\t\t\treturn NULL;\n-\t\tif (type == required_type) {\n-\t\t\t*size = isize;\n-\t\t\tif (actual_oid_return)\n-\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n-\t\t\treturn buffer;\n-\t\t}\n-\t\t/* Handle references */\n-\t\telse if (type == OBJ_COMMIT)\n-\t\t\tref_type = \"tree \";\n-\t\telse if (type == OBJ_TAG)\n-\t\t\tref_type = \"object \";\n-\t\telse {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tref_length = strlen(ref_type);\n-\n-\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n-\t\t    memcmp(buffer, ref_type, ref_length) ||\n-\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n-\t\t\tfree(buffer);\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tfree(buffer);\n-\t\t/* Now we have the ID of the referred-to object in\n-\t\t * actual_oid.  Check again. */\n-\t}\n-}\n-\n static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,\n \t\t\t     const void *buf, unsigned long len,\n \t\t\t     struct object_id *oid,\n@@ -2154,32 +1226,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)\n \treturn ret;\n }\n \n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags)\n-{\n-\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n-\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n-\t\t(quick ? OBJECT_INFO_QUICK : 0);\n-\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n-}\n-\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags)\n-{\n-\tif (!startup_info->have_repository)\n-\t\treturn 0;\n-\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n-}\n-\n-int repo_has_object_file(struct repository *r,\n-\t\t\t const struct object_id *oid)\n-{\n-\treturn repo_has_object_file_with_flags(r, oid, 0);\n-}\n-\n /*\n  * We can't use the normal fsck_error_function() for index_mem(),\n  * because we don't yet have a valid oid for it to report. Instead,\n@@ -2407,16 +1453,6 @@ int read_pack_header(int fd, struct pack_header *header)\n \treturn 0;\n }\n \n-void assert_oid_type(const struct object_id *oid, enum object_type expect)\n-{\n-\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n-\tif (type < 0)\n-\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n-\tif (type != expect)\n-\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n-\t\t    type_name(expect));\n-}\n-\n int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n \t\t\t\tstruct strbuf *path,\n \t\t\t\teach_loose_object_fn obj_cb,\ndiff --git a/object-file.h b/object-file.h\nindex 353d8a85c33..78c84d970a9 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -21,6 +21,29 @@ extern int fetch_if_missing;\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n+struct object_directory;\n+\n+const char *odb_loose_path(struct object_directory *odb,\n+\t\t\t   struct strbuf *buf,\n+\t\t\t   const struct object_id *oid);\n+\n+/*\n+ * Return true iff an alternate object database has a loose object\n+ * with the specified name.  This function does not respect replace\n+ * references.\n+ */\n+int has_loose_object_nonlocal(const struct object_id *);\n+\n+int has_loose_object(const struct object_id *);\n+\n+/**\n+ * format_object_header() is a thin wrapper around s xsnprintf() that\n+ * writes the initial \"<type> <obj-len>\" part of the loose object\n+ * header. It returns the size that snprintf() returns + 1.\n+ */\n+int format_object_header(char *str, size_t size, enum object_type type,\n+\t\t\t size_t objsize);\n+\n /**\n  * unpack_loose_header() initializes the data stream needed to unpack\n  * a loose object header.\n@@ -61,6 +84,29 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+int write_object_file_flags(const void *buf, unsigned long len,\n+\t\t\t    enum object_type type, struct object_id *oid,\n+\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n+static inline int write_object_file(const void *buf, unsigned long len,\n+\t\t\t\t    enum object_type type, struct object_id *oid)\n+{\n+\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n+}\n+\n+struct input_stream {\n+\tconst void *(*read)(struct input_stream *, unsigned long *len);\n+\tvoid *data;\n+\tint is_finished;\n+};\n+\n+int write_object_file_literally(const void *buf, unsigned long len,\n+\t\t\t\tconst char *type, struct object_id *oid,\n+\t\t\t\tunsigned flags);\n+int stream_loose_object(struct input_stream *in_stream, size_t len,\n+\t\t\tstruct object_id *oid);\n+\n+int force_object_loose(const struct object_id *oid, time_t mtime);\n+\n /**\n  * With in-core object data in \"buf\", rehash it to make sure the\n  * object name actually matches \"oid\" to detect object corruption.\n@@ -79,6 +125,10 @@ int check_object_signature(struct repository *r, const struct object_id *oid,\n  */\n int stream_object_signature(struct repository *r, const struct object_id *oid);\n \n+int loose_object_info(struct repository *r,\n+\t\t      const struct object_id *oid,\n+\t\t      struct object_info *oi, int flags);\n+\n enum finalize_object_file_flags {\n \tFOF_SKIP_COLLISION_CHECK = 1,\n };\n@@ -90,10 +140,18 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,\n /* Helper to check and \"touch\" a file */\n int check_and_freshen_file(const char *fn, int freshen);\n \n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n+/*\n+ * Open the loose object at path, check its hash, and return the contents,\n+ * use the \"oi\" argument to assert things about the object, or e.g. populate its\n+ * type, and size. If the object is a blob, then \"contents\" may return NULL,\n+ * to allow streaming of large blobs.\n+ *\n+ * Returns 0 on success, negative on error (details may be written to stderr).\n+ */\n+int read_loose_object(const char *path,\n+\t\t      const struct object_id *expected_oid,\n+\t\t      struct object_id *real_oid,\n+\t\t      void **contents,\n+\t\t      struct object_info *oi);\n \n #endif /* OBJECT_FILE_H */\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex cd3bd5bd99f..8ae80b8a5fa 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -49,12 +49,6 @@ struct object_directory {\n \tchar *path;\n };\n \n-struct input_stream {\n-\tconst void *(*read)(struct input_stream *, unsigned long *len);\n-\tvoid *data;\n-\tint is_finished;\n-};\n-\n void prepare_alt_odb(struct repository *r);\n int has_alt_odb(struct repository *r);\n char *compute_alternate_path(const char *path, struct strbuf *err);\n@@ -273,21 +267,6 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n \t\t      unsigned long len, enum object_type type,\n \t\t      struct object_id *oid);\n \n-int write_object_file_flags(const void *buf, unsigned long len,\n-\t\t\t    enum object_type type, struct object_id *oid,\n-\t\t\t    struct object_id *comapt_oid_in, unsigned flags);\n-static inline int write_object_file(const void *buf, unsigned long len,\n-\t\t\t\t    enum object_type type, struct object_id *oid)\n-{\n-\treturn write_object_file_flags(buf, len, type, oid, NULL, 0);\n-}\n-\n-int write_object_file_literally(const void *buf, unsigned long len,\n-\t\t\t\tconst char *type, struct object_id *oid,\n-\t\t\t\tunsigned flags);\n-int stream_loose_object(struct input_stream *in_stream, size_t len,\n-\t\t\tstruct object_id *oid);\n-\n /*\n  * Add an object file to the in-memory object store, without writing it\n  * to disk.\n@@ -299,8 +278,6 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,\n int pretend_object_file(void *, unsigned long, enum object_type,\n \t\t\tstruct object_id *oid);\n \n-int force_object_loose(const struct object_id *oid, time_t mtime);\n-\n struct object_info {\n \t/* Request */\n \tenum object_type *typep;\n@@ -364,20 +341,6 @@ int oid_object_info_extended(struct repository *r,\n \t\t\t     const struct object_id *,\n \t\t\t     struct object_info *, unsigned flags);\n \n-/*\n- * Open the loose object at path, check its hash, and return the contents,\n- * use the \"oi\" argument to assert things about the object, or e.g. populate its\n- * type, and size. If the object is a blob, then \"contents\" may return NULL,\n- * to allow streaming of large blobs.\n- *\n- * Returns 0 on success, negative on error (details may be written to stderr).\n- */\n-int read_loose_object(const char *path,\n-\t\t      const struct object_id *expected_oid,\n-\t\t      struct object_id *real_oid,\n-\t\t      void **contents,\n-\t\t      struct object_info *oi);\n-\n /* Retry packed storage after checking packed and loose storage */\n #define HAS_OBJECT_RECHECK_PACKED 1\n \n@@ -405,23 +368,6 @@ int repo_has_object_file(struct repository *r, const struct object_id *oid);\n int repo_has_object_file_with_flags(struct repository *r,\n \t\t\t\t    const struct object_id *oid, int flags);\n \n-/*\n- * Return true iff an alternate object database has a loose object\n- * with the specified name.  This function does not respect replace\n- * references.\n- */\n-int has_loose_object_nonlocal(const struct object_id *);\n-\n-int has_loose_object(const struct object_id *);\n-\n-/**\n- * format_object_header() is a thin wrapper around s xsnprintf() that\n- * writes the initial \"<type> <obj-len>\" part of the loose object\n- * header. It returns the size that snprintf() returns + 1.\n- */\n-int format_object_header(char *str, size_t size, enum object_type type,\n-\t\t\t size_t objsize);\n-\n void assert_oid_type(const struct object_id *oid, enum object_type expect);\n \n /*\n@@ -553,4 +499,10 @@ int for_each_object_in_pack(struct packed_git *p,\n int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n \t\t\t   void *data, enum for_each_object_flags flags);\n \n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n+\n #endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nnew file mode 100644\nindex 00000000000..a2004dca15a\n--- /dev/null\n+++ b/object-store.c\n@@ -0,0 +1,972 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"abspath.h\"\n+#include \"config.h\"\n+#include \"environment.h\"\n+#include \"gettext.h\"\n+#include \"hex.h\"\n+#include \"lockfile.h\"\n+#include \"object-file-convert.h\"\n+#include \"object-file.h\"\n+#include \"object-store.h\"\n+#include \"packfile.h\"\n+#include \"path.h\"\n+#include \"promisor-remote.h\"\n+#include \"quote.h\"\n+#include \"replace-object.h\"\n+#include \"run-command.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"strvec.h\"\n+#include \"submodule.h\"\n+#include \"write-or-die.h\"\n+\n+/*\n+ * This is meant to hold a *small* number of objects that you would\n+ * want repo_read_object_file() to be able to return, but yet you do not want\n+ * to write them into the object store (e.g. a browse-only\n+ * application).\n+ */\n+static struct cached_object_entry {\n+\tstruct object_id oid;\n+\tstruct cached_object {\n+\t\tenum object_type type;\n+\t\tconst void *buf;\n+\t\tunsigned long size;\n+\t} value;\n+} *cached_objects;\n+static int cached_object_nr, cached_object_alloc;\n+\n+static const struct cached_object *find_cached_object(const struct object_id *oid)\n+{\n+\tstatic const struct cached_object empty_tree = {\n+\t\t.type = OBJ_TREE,\n+\t\t.buf = \"\",\n+\t};\n+\tint i;\n+\tconst struct cached_object_entry *co = cached_objects;\n+\n+\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\t\tif (oideq(&co->oid, oid))\n+\t\t\treturn &co->value;\n+\t}\n+\tif (oideq(oid, the_hash_algo->empty_tree))\n+\t\treturn &empty_tree;\n+\treturn NULL;\n+}\n+\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)\n+{\n+\tint fd;\n+\t/*\n+\t * we let the umask do its job, don't try to be more\n+\t * restrictive except to remove write permission.\n+\t */\n+\tint mode = 0444;\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tfd = git_mkstemp_mode(temp_filename->buf, mode);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\t/* some mkstemp implementations erase temp_filename on failure */\n+\trepo_git_path_replace(the_repository, temp_filename, \"objects/%s\", pattern);\n+\tsafe_create_leading_directories(the_repository, temp_filename->buf);\n+\treturn xmkstemp_mode(temp_filename->buf, mode);\n+}\n+\n+int odb_pack_keep(const char *name)\n+{\n+\tint fd;\n+\n+\tfd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+\tif (0 <= fd)\n+\t\treturn fd;\n+\n+\t/* slow path */\n+\tsafe_create_leading_directories_const(the_repository, name);\n+\treturn open(name, O_RDWR|O_CREAT|O_EXCL, 0600);\n+}\n+\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid)\n+{\n+\treturn odb_loose_path(r->objects->odb, buf, oid);\n+}\n+\n+/*\n+ * Return non-zero iff the path is usable as an alternate object database.\n+ */\n+static int alt_odb_usable(struct raw_object_store *o,\n+\t\t\t  struct strbuf *path,\n+\t\t\t  const char *normalized_objdir, khiter_t *pos)\n+{\n+\tint r;\n+\n+\t/* Detect cases where alternate disappeared */\n+\tif (!is_directory(path->buf)) {\n+\t\terror(_(\"object directory %s does not exist; \"\n+\t\t\t\"check .git/objects/info/alternates\"),\n+\t\t      path->buf);\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Prevent the common mistake of listing the same\n+\t * thing twice, or object directory itself.\n+\t */\n+\tif (!o->odb_by_path) {\n+\t\tkhiter_t p;\n+\n+\t\to->odb_by_path = kh_init_odb_path_map();\n+\t\tassert(!o->odb->next);\n+\t\tp = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);\n+\t\tassert(r == 1); /* never used */\n+\t\tkh_value(o->odb_by_path, p) = o->odb;\n+\t}\n+\tif (fspatheq(path->buf, normalized_objdir))\n+\t\treturn 0;\n+\t*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);\n+\t/* r: 0 = exists, 1 = never used, 2 = deleted */\n+\treturn r == 0 ? 0 : 1;\n+}\n+\n+/*\n+ * Prepare alternate object database registry.\n+ *\n+ * The variable alt_odb_list points at the list of struct\n+ * object_directory.  The elements on this list come from\n+ * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT\n+ * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,\n+ * whose contents is similar to that environment variable but can be\n+ * LF separated.  Its base points at a statically allocated buffer that\n+ * contains \"/the/directory/corresponding/to/.git/objects/...\", while\n+ * its name points just after the slash at the end of \".git/objects/\"\n+ * in the example above, and has enough space to hold all hex characters\n+ * of the object ID, an extra slash for the first level indirection, and\n+ * the terminating NUL.\n+ */\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth);\n+static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,\n+\tconst char *relative_base, int depth, const char *normalized_objdir)\n+{\n+\tstruct object_directory *ent;\n+\tstruct strbuf pathbuf = STRBUF_INIT;\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\tkhiter_t pos;\n+\tint ret = -1;\n+\n+\tif (!is_absolute_path(entry->buf) && relative_base) {\n+\t\tstrbuf_realpath(&pathbuf, relative_base, 1);\n+\t\tstrbuf_addch(&pathbuf, '/');\n+\t}\n+\tstrbuf_addbuf(&pathbuf, entry);\n+\n+\tif (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {\n+\t\terror(_(\"unable to normalize alternate object path: %s\"),\n+\t\t      pathbuf.buf);\n+\t\tgoto error;\n+\t}\n+\tstrbuf_swap(&pathbuf, &tmp);\n+\n+\t/*\n+\t * The trailing slash after the directory name is given by\n+\t * this function at the end. Remove duplicates.\n+\t */\n+\twhile (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')\n+\t\tstrbuf_setlen(&pathbuf, pathbuf.len - 1);\n+\n+\tif (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))\n+\t\tgoto error;\n+\n+\tCALLOC_ARRAY(ent, 1);\n+\t/* pathbuf.buf is already in r->objects->odb_by_path */\n+\tent->path = strbuf_detach(&pathbuf, NULL);\n+\n+\t/* add the alternate entry */\n+\t*r->objects->odb_tail = ent;\n+\tr->objects->odb_tail = &(ent->next);\n+\tent->next = NULL;\n+\tassert(r->objects->odb_by_path);\n+\tkh_value(r->objects->odb_by_path, pos) = ent;\n+\n+\t/* recursively add alternates */\n+\tread_info_alternates(r, ent->path, depth + 1);\n+\tret = 0;\n+ error:\n+\tstrbuf_release(&tmp);\n+\tstrbuf_release(&pathbuf);\n+\treturn ret;\n+}\n+\n+static const char *parse_alt_odb_entry(const char *string,\n+\t\t\t\t       int sep,\n+\t\t\t\t       struct strbuf *out)\n+{\n+\tconst char *end;\n+\n+\tstrbuf_reset(out);\n+\n+\tif (*string == '#') {\n+\t\t/* comment; consume up to next separator */\n+\t\tend = strchrnul(string, sep);\n+\t} else if (*string == '\"' && !unquote_c_style(out, string, &end)) {\n+\t\t/*\n+\t\t * quoted path; unquote_c_style has copied the\n+\t\t * data for us and set \"end\". Broken quoting (e.g.,\n+\t\t * an entry that doesn't end with a quote) falls\n+\t\t * back to the unquoted case below.\n+\t\t */\n+\t} else {\n+\t\t/* normal, unquoted path */\n+\t\tend = strchrnul(string, sep);\n+\t\tstrbuf_add(out, string, end - string);\n+\t}\n+\n+\tif (*end)\n+\t\tend++;\n+\treturn end;\n+}\n+\n+static void link_alt_odb_entries(struct repository *r, const char *alt,\n+\t\t\t\t int sep, const char *relative_base, int depth)\n+{\n+\tstruct strbuf objdirbuf = STRBUF_INIT;\n+\tstruct strbuf entry = STRBUF_INIT;\n+\n+\tif (!alt || !*alt)\n+\t\treturn;\n+\n+\tif (depth > 5) {\n+\t\terror(_(\"%s: ignoring alternate object stores, nesting too deep\"),\n+\t\t\t\trelative_base);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_realpath(&objdirbuf, r->objects->odb->path, 1);\n+\n+\twhile (*alt) {\n+\t\talt = parse_alt_odb_entry(alt, sep, &entry);\n+\t\tif (!entry.len)\n+\t\t\tcontinue;\n+\t\tlink_alt_odb_entry(r, &entry,\n+\t\t\t\t   relative_base, depth, objdirbuf.buf);\n+\t}\n+\tstrbuf_release(&entry);\n+\tstrbuf_release(&objdirbuf);\n+}\n+\n+static void read_info_alternates(struct repository *r,\n+\t\t\t\t const char *relative_base,\n+\t\t\t\t int depth)\n+{\n+\tchar *path;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tpath = xstrfmt(\"%s/info/alternates\", relative_base);\n+\tif (strbuf_read_file(&buf, path, 1024) < 0) {\n+\t\twarn_on_fopen_errors(path);\n+\t\tfree(path);\n+\t\treturn;\n+\t}\n+\n+\tlink_alt_odb_entries(r, buf.buf, '\\n', relative_base, depth);\n+\tstrbuf_release(&buf);\n+\tfree(path);\n+}\n+\n+void add_to_alternates_file(const char *reference)\n+{\n+\tstruct lock_file lock = LOCK_INIT;\n+\tchar *alts = repo_git_path(the_repository, \"objects/info/alternates\");\n+\tFILE *in, *out;\n+\tint found = 0;\n+\n+\thold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);\n+\tout = fdopen_lock_file(&lock, \"w\");\n+\tif (!out)\n+\t\tdie_errno(_(\"unable to fdopen alternates lockfile\"));\n+\n+\tin = fopen(alts, \"r\");\n+\tif (in) {\n+\t\tstruct strbuf line = STRBUF_INIT;\n+\n+\t\twhile (strbuf_getline(&line, in) != EOF) {\n+\t\t\tif (!strcmp(reference, line.buf)) {\n+\t\t\t\tfound = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tfprintf_or_die(out, \"%s\\n\", line.buf);\n+\t\t}\n+\n+\t\tstrbuf_release(&line);\n+\t\tfclose(in);\n+\t}\n+\telse if (errno != ENOENT)\n+\t\tdie_errno(_(\"unable to read alternates file\"));\n+\n+\tif (found) {\n+\t\trollback_lock_file(&lock);\n+\t} else {\n+\t\tfprintf_or_die(out, \"%s\\n\", reference);\n+\t\tif (commit_lock_file(&lock))\n+\t\t\tdie_errno(_(\"unable to move new alternates file into place\"));\n+\t\tif (the_repository->objects->loaded_alternates)\n+\t\t\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t\t\t     '\\n', NULL, 0);\n+\t}\n+\tfree(alts);\n+}\n+\n+void add_to_alternates_memory(const char *reference)\n+{\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\tlink_alt_odb_entries(the_repository, reference,\n+\t\t\t     '\\n', NULL, 0);\n+}\n+\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)\n+{\n+\tstruct object_directory *new_odb;\n+\n+\t/*\n+\t * Make sure alternates are initialized, or else our entry may be\n+\t * overwritten when they are.\n+\t */\n+\tprepare_alt_odb(the_repository);\n+\n+\t/*\n+\t * Make a new primary odb and link the old primary ODB in as an\n+\t * alternate\n+\t */\n+\tnew_odb = xcalloc(1, sizeof(*new_odb));\n+\tnew_odb->path = xstrdup(dir);\n+\n+\t/*\n+\t * Disable ref updates while a temporary odb is active, since\n+\t * the objects in the database may roll back.\n+\t */\n+\tnew_odb->disable_ref_updates = 1;\n+\tnew_odb->will_destroy = will_destroy;\n+\tnew_odb->next = the_repository->objects->odb;\n+\tthe_repository->objects->odb = new_odb;\n+\treturn new_odb->next;\n+}\n+\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n+{\n+\tstruct object_directory *cur_odb = the_repository->objects->odb;\n+\n+\tif (strcmp(old_path, cur_odb->path))\n+\t\tBUG(\"expected %s as primary object store; found %s\",\n+\t\t    old_path, cur_odb->path);\n+\n+\tif (cur_odb->next != restore_odb)\n+\t\tBUG(\"we expect the old primary object store to be the first alternate\");\n+\n+\tthe_repository->objects->odb = restore_odb;\n+\tfree_object_directory(cur_odb);\n+}\n+\n+/*\n+ * Compute the exact path an alternate is at and returns it. In case of\n+ * error NULL is returned and the human readable error is added to `err`\n+ * `path` may be relative and should point to $GIT_DIR.\n+ * `err` must not be null.\n+ */\n+char *compute_alternate_path(const char *path, struct strbuf *err)\n+{\n+\tchar *ref_git = NULL;\n+\tconst char *repo;\n+\tint seen_error = 0;\n+\n+\tref_git = real_pathdup(path, 0);\n+\tif (!ref_git) {\n+\t\tseen_error = 1;\n+\t\tstrbuf_addf(err, _(\"path '%s' does not exist\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\trepo = read_gitfile(ref_git);\n+\tif (!repo)\n+\t\trepo = read_gitfile(mkpath(\"%s/.git\", ref_git));\n+\tif (repo) {\n+\t\tfree(ref_git);\n+\t\tref_git = xstrdup(repo);\n+\t}\n+\n+\tif (!repo && is_directory(mkpath(\"%s/.git/objects\", ref_git))) {\n+\t\tchar *ref_git_git = mkpathdup(\"%s/.git\", ref_git);\n+\t\tfree(ref_git);\n+\t\tref_git = ref_git_git;\n+\t} else if (!is_directory(mkpath(\"%s/objects\", ref_git))) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tseen_error = 1;\n+\t\tif (get_common_dir(&sb, ref_git)) {\n+\t\t\tstrbuf_addf(err,\n+\t\t\t\t    _(\"reference repository '%s' as a linked \"\n+\t\t\t\t      \"checkout is not supported yet.\"),\n+\t\t\t\t    path);\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is not a \"\n+\t\t\t\t\t\"local repository.\"), path);\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/shallow\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err, _(\"reference repository '%s' is shallow\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+\tif (!access(mkpath(\"%s/info/grafts\", ref_git), F_OK)) {\n+\t\tstrbuf_addf(err,\n+\t\t\t    _(\"reference repository '%s' is grafted\"),\n+\t\t\t    path);\n+\t\tseen_error = 1;\n+\t\tgoto out;\n+\t}\n+\n+out:\n+\tif (seen_error) {\n+\t\tFREE_AND_NULL(ref_git);\n+\t}\n+\n+\treturn ref_git;\n+}\n+\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir)\n+{\n+\tstruct object_directory *odb;\n+\tchar *obj_dir_real = real_pathdup(obj_dir, 1);\n+\tstruct strbuf odb_path_real = STRBUF_INIT;\n+\n+\tprepare_alt_odb(r);\n+\tfor (odb = r->objects->odb; odb; odb = odb->next) {\n+\t\tstrbuf_realpath(&odb_path_real, odb->path, 1);\n+\t\tif (!strcmp(obj_dir_real, odb_path_real.buf))\n+\t\t\tbreak;\n+\t}\n+\n+\tfree(obj_dir_real);\n+\tstrbuf_release(&odb_path_real);\n+\n+\tif (!odb)\n+\t\tdie(_(\"could not find object directory matching %s\"), obj_dir);\n+\treturn odb;\n+}\n+\n+static void fill_alternate_refs_command(struct child_process *cmd,\n+\t\t\t\t\tconst char *repo_path)\n+{\n+\tconst char *value;\n+\n+\tif (!git_config_get_value(\"core.alternateRefsCommand\", &value)) {\n+\t\tcmd->use_shell = 1;\n+\n+\t\tstrvec_push(&cmd->args, value);\n+\t\tstrvec_push(&cmd->args, repo_path);\n+\t} else {\n+\t\tcmd->git_cmd = 1;\n+\n+\t\tstrvec_pushf(&cmd->args, \"--git-dir=%s\", repo_path);\n+\t\tstrvec_push(&cmd->args, \"for-each-ref\");\n+\t\tstrvec_push(&cmd->args, \"--format=%(objectname)\");\n+\n+\t\tif (!git_config_get_value(\"core.alternateRefsPrefixes\", &value)) {\n+\t\t\tstrvec_push(&cmd->args, \"--\");\n+\t\t\tstrvec_split(&cmd->args, value);\n+\t\t}\n+\t}\n+\n+\tstrvec_pushv(&cmd->env, (const char **)local_repo_env);\n+\tcmd->out = -1;\n+}\n+\n+static void read_alternate_refs(const char *path,\n+\t\t\t\talternate_ref_fn *cb,\n+\t\t\t\tvoid *data)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tstruct strbuf line = STRBUF_INIT;\n+\tFILE *fh;\n+\n+\tfill_alternate_refs_command(&cmd, path);\n+\n+\tif (start_command(&cmd))\n+\t\treturn;\n+\n+\tfh = xfdopen(cmd.out, \"r\");\n+\twhile (strbuf_getline_lf(&line, fh) != EOF) {\n+\t\tstruct object_id oid;\n+\t\tconst char *p;\n+\n+\t\tif (parse_oid_hex(line.buf, &oid, &p) || *p) {\n+\t\t\twarning(_(\"invalid line while parsing alternate refs: %s\"),\n+\t\t\t\tline.buf);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcb(&oid, data);\n+\t}\n+\n+\tfclose(fh);\n+\tfinish_command(&cmd);\n+\tstrbuf_release(&line);\n+}\n+\n+struct alternate_refs_data {\n+\talternate_ref_fn *fn;\n+\tvoid *data;\n+};\n+\n+static int refs_from_alternate_cb(struct object_directory *e,\n+\t\t\t\t  void *data)\n+{\n+\tstruct strbuf path = STRBUF_INIT;\n+\tsize_t base_len;\n+\tstruct alternate_refs_data *cb = data;\n+\n+\tif (!strbuf_realpath(&path, e->path, 0))\n+\t\tgoto out;\n+\tif (!strbuf_strip_suffix(&path, \"/objects\"))\n+\t\tgoto out;\n+\tbase_len = path.len;\n+\n+\t/* Is this a git repository with refs? */\n+\tstrbuf_addstr(&path, \"/refs\");\n+\tif (!is_directory(path.buf))\n+\t\tgoto out;\n+\tstrbuf_setlen(&path, base_len);\n+\n+\tread_alternate_refs(path.buf, cb->fn, cb->data);\n+\n+out:\n+\tstrbuf_release(&path);\n+\treturn 0;\n+}\n+\n+void for_each_alternate_ref(alternate_ref_fn fn, void *data)\n+{\n+\tstruct alternate_refs_data cb;\n+\tcb.fn = fn;\n+\tcb.data = data;\n+\tforeach_alt_odb(refs_from_alternate_cb, &cb);\n+}\n+\n+int foreach_alt_odb(alt_odb_fn fn, void *cb)\n+{\n+\tstruct object_directory *ent;\n+\tint r = 0;\n+\n+\tprepare_alt_odb(the_repository);\n+\tfor (ent = the_repository->objects->odb->next; ent; ent = ent->next) {\n+\t\tr = fn(ent, cb);\n+\t\tif (r)\n+\t\t\tbreak;\n+\t}\n+\treturn r;\n+}\n+\n+void prepare_alt_odb(struct repository *r)\n+{\n+\tif (r->objects->loaded_alternates)\n+\t\treturn;\n+\n+\tlink_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);\n+\n+\tread_info_alternates(r, r->objects->odb->path, 0);\n+\tr->objects->loaded_alternates = 1;\n+}\n+\n+int has_alt_odb(struct repository *r)\n+{\n+\tprepare_alt_odb(r);\n+\treturn !!r->objects->odb->next;\n+}\n+\n+int obj_read_use_lock = 0;\n+pthread_mutex_t obj_read_mutex;\n+\n+void enable_obj_read_lock(void)\n+{\n+\tif (obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 1;\n+\tinit_recursive_mutex(&obj_read_mutex);\n+}\n+\n+void disable_obj_read_lock(void)\n+{\n+\tif (!obj_read_use_lock)\n+\t\treturn;\n+\n+\tobj_read_use_lock = 0;\n+\tpthread_mutex_destroy(&obj_read_mutex);\n+}\n+\n+int fetch_if_missing = 1;\n+\n+static int do_oid_object_info_extended(struct repository *r,\n+\t\t\t\t       const struct object_id *oid,\n+\t\t\t\t       struct object_info *oi, unsigned flags)\n+{\n+\tstatic struct object_info blank_oi = OBJECT_INFO_INIT;\n+\tconst struct cached_object *co;\n+\tstruct pack_entry e;\n+\tint rtype;\n+\tconst struct object_id *real = oid;\n+\tint already_retried = 0;\n+\n+\n+\tif (flags & OBJECT_INFO_LOOKUP_REPLACE)\n+\t\treal = lookup_replace_object(r, oid);\n+\n+\tif (is_null_oid(real))\n+\t\treturn -1;\n+\n+\tif (!oi)\n+\t\toi = &blank_oi;\n+\n+\tco = find_cached_object(real);\n+\tif (co) {\n+\t\tif (oi->typep)\n+\t\t\t*(oi->typep) = co->type;\n+\t\tif (oi->sizep)\n+\t\t\t*(oi->sizep) = co->size;\n+\t\tif (oi->disk_sizep)\n+\t\t\t*(oi->disk_sizep) = 0;\n+\t\tif (oi->delta_base_oid)\n+\t\t\toidclr(oi->delta_base_oid, the_repository->hash_algo);\n+\t\tif (oi->type_name)\n+\t\t\tstrbuf_addstr(oi->type_name, type_name(co->type));\n+\t\tif (oi->contentp)\n+\t\t\t*oi->contentp = xmemdupz(co->buf, co->size);\n+\t\toi->whence = OI_CACHED;\n+\t\treturn 0;\n+\t}\n+\n+\twhile (1) {\n+\t\tif (find_pack_entry(r, real, &e))\n+\t\t\tbreak;\n+\n+\t\t/* Most likely it's a loose object. */\n+\t\tif (!loose_object_info(r, real, oi, flags))\n+\t\t\treturn 0;\n+\n+\t\t/* Not a loose object; someone else may have just packed it. */\n+\t\tif (!(flags & OBJECT_INFO_QUICK)) {\n+\t\t\treprepare_packed_git(r);\n+\t\t\tif (find_pack_entry(r, real, &e))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * If r is the_repository, this might be an attempt at\n+\t\t * accessing a submodule object as if it were in the_repository\n+\t\t * (having called add_submodule_odb() on that submodule's ODB).\n+\t\t * If any such ODBs exist, register them and try again.\n+\t\t */\n+\t\tif (r == the_repository &&\n+\t\t    register_all_submodule_odb_as_alternates())\n+\t\t\t/* We added some alternates; retry */\n+\t\t\tcontinue;\n+\n+\t\t/* Check if it is a missing object */\n+\t\tif (fetch_if_missing && repo_has_promisor_remote(r) &&\n+\t\t    !already_retried &&\n+\t\t    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {\n+\t\t\tpromisor_remote_get_direct(r, real, 1);\n+\t\t\talready_retried = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (flags & OBJECT_INFO_DIE_IF_CORRUPT) {\n+\t\t\tconst struct packed_git *p;\n+\t\t\tif ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))\n+\t\t\t\tdie(_(\"replacement %s not found for %s\"),\n+\t\t\t\t    oid_to_hex(real), oid_to_hex(oid));\n+\t\t\tif ((p = has_packed_and_bad(r, real)))\n+\t\t\t\tdie(_(\"packed object %s (stored in %s) is corrupt\"),\n+\t\t\t\t    oid_to_hex(real), p->pack_name);\n+\t\t}\n+\t\treturn -1;\n+\t}\n+\n+\tif (oi == &blank_oi)\n+\t\t/*\n+\t\t * We know that the caller doesn't actually need the\n+\t\t * information below, so return early.\n+\t\t */\n+\t\treturn 0;\n+\trtype = packed_object_info(r, e.p, e.offset, oi);\n+\tif (rtype < 0) {\n+\t\tmark_bad_packed_object(e.p, real);\n+\t\treturn do_oid_object_info_extended(r, real, oi, 0);\n+\t} else if (oi->whence == OI_PACKED) {\n+\t\toi->u.packed.offset = e.offset;\n+\t\toi->u.packed.pack = e.p;\n+\t\toi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||\n+\t\t\t\t\t rtype == OBJ_OFS_DELTA);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int oid_object_info_convert(struct repository *r,\n+\t\t\t\t   const struct object_id *input_oid,\n+\t\t\t\t   struct object_info *input_oi, unsigned flags)\n+{\n+\tconst struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];\n+\tint do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;\n+\tstruct strbuf type_name = STRBUF_INIT;\n+\tstruct object_id oid, delta_base_oid;\n+\tstruct object_info new_oi, *oi;\n+\tunsigned long size;\n+\tvoid *content;\n+\tint ret;\n+\n+\tif (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {\n+\t\tif (do_die)\n+\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t    oid_to_hex(input_oid), the_hash_algo->name);\n+\t\treturn -1;\n+\t}\n+\n+\t/* Is new_oi needed? */\n+\toi = input_oi;\n+\tif (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||\n+\t\t\t input_oi->contentp)) {\n+\t\tnew_oi = *input_oi;\n+\t\t/* Does delta_base_oid need to be converted? */\n+\t\tif (input_oi->delta_base_oid)\n+\t\t\tnew_oi.delta_base_oid = &delta_base_oid;\n+\t\t/* Will the attributes differ when converted? */\n+\t\tif (input_oi->sizep || input_oi->contentp) {\n+\t\t\tnew_oi.contentp = &content;\n+\t\t\tnew_oi.sizep = &size;\n+\t\t\tnew_oi.type_name = &type_name;\n+\t\t}\n+\t\toi = &new_oi;\n+\t}\n+\n+\tret = oid_object_info_extended(r, &oid, oi, flags);\n+\tif (ret)\n+\t\treturn -1;\n+\tif (oi == input_oi)\n+\t\treturn ret;\n+\n+\tif (new_oi.contentp) {\n+\t\tstruct strbuf outbuf = STRBUF_INIT;\n+\t\tenum object_type type;\n+\n+\t\ttype = type_from_string_gently(type_name.buf, type_name.len,\n+\t\t\t\t\t       !do_die);\n+\t\tif (type == -1)\n+\t\t\treturn -1;\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = convert_object_file(the_repository, &outbuf,\n+\t\t\t\t\t\t  the_hash_algo, input_algo,\n+\t\t\t\t\t\t  content, size, type, !do_die);\n+\t\t\tfree(content);\n+\t\t\tif (ret == -1)\n+\t\t\t\treturn -1;\n+\t\t\tsize = outbuf.len;\n+\t\t\tcontent = strbuf_detach(&outbuf, NULL);\n+\t\t}\n+\t\tif (input_oi->sizep)\n+\t\t\t*input_oi->sizep = size;\n+\t\tif (input_oi->contentp)\n+\t\t\t*input_oi->contentp = content;\n+\t\telse\n+\t\t\tfree(content);\n+\t\tif (input_oi->type_name)\n+\t\t\t*input_oi->type_name = type_name;\n+\t\telse\n+\t\t\tstrbuf_release(&type_name);\n+\t}\n+\tif (new_oi.delta_base_oid == &delta_base_oid) {\n+\t\tif (repo_oid_to_algop(r, &delta_base_oid, input_algo,\n+\t\t\t\t input_oi->delta_base_oid)) {\n+\t\t\tif (do_die)\n+\t\t\t\tdie(_(\"missing mapping of %s to %s\"),\n+\t\t\t\t    oid_to_hex(&delta_base_oid),\n+\t\t\t\t    input_algo->name);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\tinput_oi->whence = new_oi.whence;\n+\tinput_oi->u = new_oi.u;\n+\treturn ret;\n+}\n+\n+int oid_object_info_extended(struct repository *r, const struct object_id *oid,\n+\t\t\t     struct object_info *oi, unsigned flags)\n+{\n+\tint ret;\n+\n+\tif (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))\n+\t\treturn oid_object_info_convert(r, oid, oi, flags);\n+\n+\tobj_read_lock();\n+\tret = do_oid_object_info_extended(r, oid, oi, flags);\n+\tobj_read_unlock();\n+\treturn ret;\n+}\n+\n+\n+/* returns enum object_type or negative */\n+int oid_object_info(struct repository *r,\n+\t\t    const struct object_id *oid,\n+\t\t    unsigned long *sizep)\n+{\n+\tenum object_type type;\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\n+\toi.typep = &type;\n+\toi.sizep = sizep;\n+\tif (oid_object_info_extended(r, oid, &oi,\n+\t\t\t\t      OBJECT_INFO_LOOKUP_REPLACE) < 0)\n+\t\treturn -1;\n+\treturn type;\n+}\n+\n+int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid)\n+{\n+\tstruct cached_object_entry *co;\n+\tchar *co_buf;\n+\n+\thash_object_file(the_hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(oid))\n+\t\treturn 0;\n+\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n+\tco = &cached_objects[cached_object_nr++];\n+\tco->value.size = len;\n+\tco->value.type = type;\n+\tco_buf = xmalloc(len);\n+\tmemcpy(co_buf, buf, len);\n+\tco->value.buf = co_buf;\n+\toidcpy(&co->oid, oid);\n+\treturn 0;\n+}\n+\n+/*\n+ * This function dies on corrupt objects; the callers who want to\n+ * deal with them should arrange to call oid_object_info_extended() and give\n+ * error messages themselves.\n+ */\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size)\n+{\n+\tstruct object_info oi = OBJECT_INFO_INIT;\n+\tunsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;\n+\tvoid *data;\n+\n+\toi.typep = type;\n+\toi.sizep = size;\n+\toi.contentp = &data;\n+\tif (oid_object_info_extended(r, oid, &oi, flags))\n+\t\treturn NULL;\n+\n+\treturn data;\n+}\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *actual_oid_return)\n+{\n+\tenum object_type type;\n+\tvoid *buffer;\n+\tunsigned long isize;\n+\tstruct object_id actual_oid;\n+\n+\toidcpy(&actual_oid, oid);\n+\twhile (1) {\n+\t\tint ref_length = -1;\n+\t\tconst char *ref_type = NULL;\n+\n+\t\tbuffer = repo_read_object_file(r, &actual_oid, &type, &isize);\n+\t\tif (!buffer)\n+\t\t\treturn NULL;\n+\t\tif (type == required_type) {\n+\t\t\t*size = isize;\n+\t\t\tif (actual_oid_return)\n+\t\t\t\toidcpy(actual_oid_return, &actual_oid);\n+\t\t\treturn buffer;\n+\t\t}\n+\t\t/* Handle references */\n+\t\telse if (type == OBJ_COMMIT)\n+\t\t\tref_type = \"tree \";\n+\t\telse if (type == OBJ_TAG)\n+\t\t\tref_type = \"object \";\n+\t\telse {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tref_length = strlen(ref_type);\n+\n+\t\tif (ref_length + the_hash_algo->hexsz > isize ||\n+\t\t    memcmp(buffer, ref_type, ref_length) ||\n+\t\t    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {\n+\t\t\tfree(buffer);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tfree(buffer);\n+\t\t/* Now we have the ID of the referred-to object in\n+\t\t * actual_oid.  Check again. */\n+\t}\n+}\n+\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags)\n+{\n+\tint quick = !(flags & HAS_OBJECT_RECHECK_PACKED);\n+\tunsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |\n+\t\t(quick ? OBJECT_INFO_QUICK : 0);\n+\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;\n+}\n+\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags)\n+{\n+\tif (!startup_info->have_repository)\n+\t\treturn 0;\n+\treturn oid_object_info_extended(r, oid, NULL, flags) >= 0;\n+}\n+\n+int repo_has_object_file(struct repository *r,\n+\t\t\t const struct object_id *oid)\n+{\n+\treturn repo_has_object_file_with_flags(r, oid, 0);\n+}\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect)\n+{\n+\tenum object_type type = oid_object_info(the_repository, oid, NULL);\n+\tif (type < 0)\n+\t\tdie(_(\"%s is not a valid object\"), oid_to_hex(oid));\n+\tif (type != expect)\n+\t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n+\t\t    type_name(expect));\n+}\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516175","messageId":"20250415-pks-split-object-file-v3-6-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 06/10] object-file: split up concerns of `HASH_*` flags","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:19Z","receivedAt":"2025-04-15T09:38:26Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The functions `hash_object_file()`, `write_object_file()` and\n`index_fd()` reuse the same set of flags to alter their behaviour. This\nnot only adds confusion, but given that every function only supports a\nsubset of the flags it becomes very hard to see which flags can be\npassed to what function. Last but not least, this entangles the\nimplementation of all three function families.\n\nSplit up concerns by creating separate flags for each of the function\nfamilies.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/hash-object.c  | 23 +++++++++++++++++------\n builtin/replace.c      |  2 +-\n builtin/update-index.c |  2 +-\n bulk-checkin.c         |  6 +++---\n cache-tree.c           |  2 +-\n notes-merge.c          |  2 +-\n object-file.c          | 18 +++++++++---------\n object-file.h          | 25 +++++++++++++++++++++----\n read-cache.c           |  4 ++--\n 9 files changed, 56 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex a25f0403f44..e7c0d6afdef 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -19,6 +19,11 @@\n #include \"strbuf.h\"\n #include \"write-or-die.h\"\n \n+enum {\n+\tHASH_OBJECT_CHECK = (1 << 0),\n+\tHASH_OBJECT_WRITE = (1 << 1),\n+};\n+\n /*\n  * This is to create corrupt objects for debugging and as such it\n  * needs to bypass the data conversion performed by, and the type\n@@ -33,7 +38,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n \t\tret = -1;\n \telse\n \t\tret = write_object_file_literally(buf.buf, buf.len, type, oid,\n-\t\t\t\t\t\t flags);\n+\t\t\t\t\t\t  (flags & HASH_OBJECT_WRITE) ? WRITE_OBJECT_FILE_PERSIST : 0);\n \tclose(fd);\n \tstrbuf_release(&buf);\n \treturn ret;\n@@ -42,15 +47,21 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n \t\t    int literally)\n {\n+\tunsigned int index_flags = 0;\n \tstruct stat st;\n \tstruct object_id oid;\n \n+\tif (flags & HASH_OBJECT_WRITE)\n+\t\tindex_flags |= INDEX_WRITE_OBJECT;\n+\tif (flags & HASH_OBJECT_CHECK)\n+\t\tindex_flags |= INDEX_FORMAT_CHECK;\n+\n \tif (fstat(fd, &st) < 0 ||\n \t    (literally\n \t     ? hash_literally(&oid, fd, type, flags)\n \t     : index_fd(the_repository->index, &oid, fd, &st,\n-\t\t\ttype_from_string(type), path, flags)))\n-\t\tdie((flags & HASH_WRITE_OBJECT)\n+\t\t\ttype_from_string(type), path, index_flags)))\n+\t\tdie((flags & HASH_OBJECT_WRITE)\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n \tprintf(\"%s\\n\", oid_to_hex(&oid));\n@@ -102,13 +113,13 @@ int cmd_hash_object(int argc,\n \tint no_filters = 0;\n \tint literally = 0;\n \tint nongit = 0;\n-\tunsigned flags = HASH_FORMAT_CHECK;\n+\tunsigned flags = HASH_OBJECT_CHECK;\n \tconst char *vpath = NULL;\n \tchar *vpath_free = NULL;\n \tconst struct option hash_object_options[] = {\n \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n-\t\t\tHASH_WRITE_OBJECT),\n+\t\t\tHASH_OBJECT_WRITE),\n \t\tOPT_COUNTUP( 0 , \"stdin\", &hashstdin, N_(\"read the object from stdin\")),\n \t\tOPT_BOOL( 0 , \"stdin-paths\", &stdin_paths, N_(\"read file names from stdin\")),\n \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n@@ -122,7 +133,7 @@ int cmd_hash_object(int argc,\n \targc = parse_options(argc, argv, prefix, hash_object_options,\n \t\t\t     hash_object_usage, 0);\n \n-\tif (flags & HASH_WRITE_OBJECT)\n+\tif (flags & HASH_OBJECT_WRITE)\n \t\tprefix = setup_git_directory();\n \telse\n \t\tprefix = setup_git_directory_gently(&nongit);\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 15ec0922ce1..2b4fc9a68b3 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -305,7 +305,7 @@ static int import_object(struct object_id *oid, enum object_type type,\n \t\tstrbuf_release(&result);\n \t} else {\n \t\tstruct stat st;\n-\t\tint flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;\n+\t\tint flags = INDEX_FORMAT_CHECK | INDEX_WRITE_OBJECT;\n \n \t\tif (fstat(fd, &st) < 0) {\n \t\t\terror_errno(_(\"unable to fstat %s\"), filename);\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex b2f6b1a3fbb..f0cf964294d 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -304,7 +304,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len\n \tce->ce_mode = ce_mode_from_stat(old, st->st_mode);\n \n \tif (index_path(the_repository->index, &ce->oid, path, st,\n-\t\t       info_only ? 0 : HASH_WRITE_OBJECT)) {\n+\t\t       info_only ? 0 : INDEX_WRITE_OBJECT)) {\n \t\tdiscard_cache_entry(ce);\n \t\treturn -1;\n \t}\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 23ac00ea0a6..309201a76a6 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -171,7 +171,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n \tunsigned char obuf[16384];\n \tunsigned hdrlen;\n \tint status = Z_OK;\n-\tint write_object = (flags & HASH_WRITE_OBJECT);\n+\tint write_object = (flags & INDEX_WRITE_OBJECT);\n \toff_t offset = 0;\n \n \tgit_deflate_init(&s, pack_compression_level);\n@@ -241,7 +241,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n static void prepare_to_stream(struct bulk_checkin_packfile *state,\n \t\t\t      unsigned flags)\n {\n-\tif (!(flags & HASH_WRITE_OBJECT) || state->f)\n+\tif (!(flags & INDEX_WRITE_OBJECT) || state->f)\n \t\treturn;\n \n \tstate->f = create_tmp_packfile(the_repository, &state->pack_tmp_name);\n@@ -275,7 +275,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \tgit_hash_update(&ctx, obuf, header_len);\n \n \t/* Note: idx is non-NULL when we are writing */\n-\tif ((flags & HASH_WRITE_OBJECT) != 0) {\n+\tif ((flags & INDEX_WRITE_OBJECT) != 0) {\n \t\tCALLOC_ARRAY(idx, 1);\n \n \t\tprepare_to_stream(state, flags);\ndiff --git a/cache-tree.c b/cache-tree.c\nindex bcbcad3d61a..4c8167ea927 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -452,7 +452,7 @@ static int update_one(struct cache_tree *it,\n \t\t\t\t OBJ_TREE, &it->oid);\n \t} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,\n \t\t\t\t\t   &it->oid, NULL, flags & WRITE_TREE_SILENT\n-\t\t\t\t\t   ? HASH_SILENT : 0)) {\n+\t\t\t\t\t   ? WRITE_OBJECT_FILE_SILENT : 0)) {\n \t\tstrbuf_release(&buffer);\n \t\treturn -1;\n \t}\ndiff --git a/notes-merge.c b/notes-merge.c\nindex fce45043655..520b92942cd 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -729,7 +729,7 @@ int notes_merge_commit(struct notes_merge_options *o,\n \t\t/* write file as blob, and add to partial_tree */\n \t\tif (stat(path.buf, &st))\n \t\t\tdie_errno(\"Failed to stat '%s'\", path.buf);\n-\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))\n+\t\tif (index_path(o->repo->index, &blob_oid, path.buf, &st, INDEX_WRITE_OBJECT))\n \t\t\tdie(\"Failed to write blob object from '%s'\", path.buf);\n \t\tif (add_note(partial_tree, &obj_oid, &blob_oid, NULL))\n \t\t\tdie(\"Failed to add resolved note '%s' to notes tree\",\ndiff --git a/object-file.c b/object-file.c\nindex baa828822ea..2051991f4de 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -33,9 +33,9 @@\n \n static int get_conv_flags(unsigned flags)\n {\n-\tif (flags & HASH_RENORMALIZE)\n+\tif (flags & INDEX_RENORMALIZE)\n \t\treturn CONV_EOL_RENORMALIZE;\n-\telse if (flags & HASH_WRITE_OBJECT)\n+\telse if (flags & INDEX_WRITE_OBJECT)\n \t\treturn global_conv_flags_eol | CONV_WRITE_OBJECT;\n \telse\n \t\treturn 0;\n@@ -835,7 +835,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n \n \tfd = create_tmpfile(tmp_file, filename);\n \tif (fd < 0) {\n-\t\tif (flags & HASH_SILENT)\n+\t\tif (flags & WRITE_OBJECT_FILE_SILENT)\n \t\t\treturn -1;\n \t\telse if (errno == EACCES)\n \t\t\treturn error(_(\"insufficient permission for adding \"\n@@ -967,7 +967,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,\n \t\tutb.actime = mtime;\n \t\tutb.modtime = mtime;\n \t\tif (utime(tmp_file.buf, &utb) < 0 &&\n-\t\t    !(flags & HASH_SILENT))\n+\t\t    !(flags & WRITE_OBJECT_FILE_SILENT))\n \t\t\twarning_errno(_(\"failed utime() on %s\"), tmp_file.buf);\n \t}\n \n@@ -1179,7 +1179,7 @@ int write_object_file_literally(const void *buf, unsigned long len,\n \twrite_object_file_prepare_literally(the_hash_algo, buf, len, type,\n \t\t\t\t\t    oid, header, &hdrlen);\n \n-\tif (!(flags & HASH_WRITE_OBJECT))\n+\tif (!(flags & WRITE_OBJECT_FILE_PERSIST))\n \t\tgoto cleanup;\n \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n \t\tgoto cleanup;\n@@ -1250,7 +1250,7 @@ static int index_mem(struct index_state *istate,\n {\n \tstruct strbuf nbuf = STRBUF_INIT;\n \tint ret = 0;\n-\tint write_object = flags & HASH_WRITE_OBJECT;\n+\tint write_object = flags & INDEX_WRITE_OBJECT;\n \n \tif (!type)\n \t\ttype = OBJ_BLOB;\n@@ -1265,7 +1265,7 @@ static int index_mem(struct index_state *istate,\n \t\t\tsize = nbuf.len;\n \t\t}\n \t}\n-\tif (flags & HASH_FORMAT_CHECK) {\n+\tif (flags & INDEX_FORMAT_CHECK) {\n \t\tstruct fsck_options opts = FSCK_OPTIONS_DEFAULT;\n \n \t\topts.strict = 1;\n@@ -1291,7 +1291,7 @@ static int index_stream_convert_blob(struct index_state *istate,\n \t\t\t\t     unsigned flags)\n {\n \tint ret = 0;\n-\tconst int write_object = flags & HASH_WRITE_OBJECT;\n+\tconst int write_object = flags & INDEX_WRITE_OBJECT;\n \tstruct strbuf sbuf = STRBUF_INIT;\n \n \tassert(path);\n@@ -1423,7 +1423,7 @@ int index_path(struct index_state *istate, struct object_id *oid,\n \tcase S_IFLNK:\n \t\tif (strbuf_readlink(&sb, path, st->st_size))\n \t\t\treturn error_errno(\"readlink(\\\"%s\\\")\", path);\n-\t\tif (!(flags & HASH_WRITE_OBJECT))\n+\t\tif (!(flags & INDEX_WRITE_OBJECT))\n \t\t\thash_object_file(the_hash_algo, sb.buf, sb.len,\n \t\t\t\t\t OBJ_BLOB, oid);\n \t\telse if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))\ndiff --git a/object-file.h b/object-file.h\nindex 78c84d970a9..c002fbe2345 100644\n--- a/object-file.h\n+++ b/object-file.h\n@@ -14,10 +14,12 @@ struct index_state;\n  */\n extern int fetch_if_missing;\n \n-#define HASH_WRITE_OBJECT 1\n-#define HASH_FORMAT_CHECK 2\n-#define HASH_RENORMALIZE  4\n-#define HASH_SILENT 8\n+enum {\n+\tINDEX_WRITE_OBJECT = (1 << 0),\n+\tINDEX_FORMAT_CHECK = (1 << 1),\n+\tINDEX_RENORMALIZE  = (1 << 2),\n+};\n+\n int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);\n int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);\n \n@@ -84,6 +86,21 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,\n struct object_info;\n int parse_loose_header(const char *hdr, struct object_info *oi);\n \n+enum {\n+\t/*\n+\t * By default, `write_object_file_literally()` does not actually write\n+\t * anything into the object store, but only computes the object ID.\n+\t * This flag changes that so that the object will be written as a loose\n+\t * object and persisted.\n+\t */\n+\tWRITE_OBJECT_FILE_PERSIST = (1 << 0),\n+\n+\t/*\n+\t * Do not print an error in case something gose wrong.\n+\t */\n+\tWRITE_OBJECT_FILE_SILENT = (1 << 1),\n+};\n+\n int write_object_file_flags(const void *buf, unsigned long len,\n \t\t\t    enum object_type type, struct object_id *oid,\n \t\t\t    struct object_id *comapt_oid_in, unsigned flags);\ndiff --git a/read-cache.c b/read-cache.c\nindex 2f9e21c897d..23028f43a11 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -706,11 +706,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,\n \tint intent_only = flags & ADD_CACHE_INTENT;\n \tint add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|\n \t\t\t  (intent_only ? ADD_CACHE_NEW_ONLY : 0));\n-\tunsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;\n+\tunsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;\n \tstruct object_id oid;\n \n \tif (flags & ADD_CACHE_RENORMALIZE)\n-\t\thash_flags |= HASH_RENORMALIZE;\n+\t\thash_flags |= INDEX_RENORMALIZE;\n \n \tif (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))\n \t\treturn error(_(\"%s: can only add regular files, symbolic links or git-directories\"), path);\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516173","messageId":"20250415-pks-split-object-file-v3-7-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 07/10] object-file: drop `index_blob_stream()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:20Z","receivedAt":"2025-04-15T09:38:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `index_blob_stream()` function is a mere wrapper around\n`index_blob_bulk_checkin()`. This has been the case since 568508e7657\n(bulk-checkin: replace fast-import based implementation, 2011-10-28),\nwhich has moved the implementation from `index_blob_stream()` (which was\nstill called `index_stream()`) into `index_bulk_checkin()` (which has\nsince been renamed to `index_blob_bulk_checkin()`).\n\nRemove the redirection by dropping the wrapper. Move the comment to\n`index_blob_bulk_checkin()` to retain its context.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n bulk-checkin.h | 15 +++++++++++++++\n object-file.c  | 26 ++------------------------\n 2 files changed, 17 insertions(+), 24 deletions(-)\n\ndiff --git a/bulk-checkin.h b/bulk-checkin.h\nindex aa7286a7b3e..7246ea58dcf 100644\n--- a/bulk-checkin.h\n+++ b/bulk-checkin.h\n@@ -9,6 +9,21 @@\n void prepare_loose_object_bulk_checkin(void);\n void fsync_loose_object_bulk_checkin(int fd, const char *filename);\n \n+/*\n+ * This creates one packfile per large blob unless bulk-checkin\n+ * machinery is \"plugged\".\n+ *\n+ * This also bypasses the usual \"convert-to-git\" dance, and that is on\n+ * purpose. We could write a streaming version of the converting\n+ * functions and insert that before feeding the data to fast-import\n+ * (or equivalent in-core API described above). However, that is\n+ * somewhat complicated, as we do not know the size of the filter\n+ * result, which we need to know beforehand when writing a git object.\n+ * Since the primary motivation for trying to stream from the working\n+ * tree file and to avoid mmaping it in core is to deal with large\n+ * binary blobs, they generally do not want to get any conversion, and\n+ * callers should avoid this code path when filters are requested.\n+ */\n int index_blob_bulk_checkin(struct object_id *oid,\n \t\t\t    int fd, size_t size,\n \t\t\t    const char *path, unsigned flags);\ndiff --git a/object-file.c b/object-file.c\nindex 2051991f4de..6084d603136 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1356,28 +1356,6 @@ static int index_core(struct index_state *istate,\n \treturn ret;\n }\n \n-/*\n- * This creates one packfile per large blob unless bulk-checkin\n- * machinery is \"plugged\".\n- *\n- * This also bypasses the usual \"convert-to-git\" dance, and that is on\n- * purpose. We could write a streaming version of the converting\n- * functions and insert that before feeding the data to fast-import\n- * (or equivalent in-core API described above). However, that is\n- * somewhat complicated, as we do not know the size of the filter\n- * result, which we need to know beforehand when writing a git object.\n- * Since the primary motivation for trying to stream from the working\n- * tree file and to avoid mmaping it in core is to deal with large\n- * binary blobs, they generally do not want to get any conversion, and\n- * callers should avoid this code path when filters are requested.\n- */\n-static int index_blob_stream(struct object_id *oid, int fd, size_t size,\n-\t\t\t     const char *path,\n-\t\t\t     unsigned flags)\n-{\n-\treturn index_blob_bulk_checkin(oid, fd, size, path, flags);\n-}\n-\n int index_fd(struct index_state *istate, struct object_id *oid,\n \t     int fd, struct stat *st,\n \t     enum object_type type, const char *path, unsigned flags)\n@@ -1398,8 +1376,8 @@ int index_fd(struct index_state *istate, struct object_id *oid,\n \t\tret = index_core(istate, oid, fd, xsize_t(st->st_size),\n \t\t\t\t type, path, flags);\n \telse\n-\t\tret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,\n-\t\t\t\t\tflags);\n+\t\tret = index_blob_bulk_checkin(oid, fd, xsize_t(st->st_size), path,\n+\t\t\t\t\t     flags);\n \tclose(fd);\n \treturn ret;\n }\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516174","messageId":"20250415-pks-split-object-file-v3-8-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 08/10] object: split out functions relating to object store subsystem","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:21Z","receivedAt":"2025-04-15T09:38:27Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Split out functions relating to the object store subsystem from\n\"object.c\". This helps us to separate concerns.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n object-store-ll.h |  3 ---\n object-store.c    | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n object.c          | 67 -------------------------------------------------------\n 3 files changed, 66 insertions(+), 70 deletions(-)\n\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8ae80b8a5fa..8bb0f33f9a8 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -92,9 +92,6 @@ struct oidtree *odb_loose_cache(struct object_directory *odb,\n /* Empty the loose object cache for the specified object directory. */\n void odb_clear_loose_cache(struct object_directory *odb);\n \n-/* Clear and free the specified object directory */\n-void free_object_directory(struct object_directory *odb);\n-\n struct packed_git {\n \tstruct hashmap_entry packmap_ent;\n \tstruct packed_git *next;\ndiff --git a/object-store.c b/object-store.c\nindex a2004dca15a..896d9ac3509 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -2,11 +2,13 @@\n \n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n+#include \"commit-graph.h\"\n #include \"config.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"lockfile.h\"\n+#include \"loose.h\"\n #include \"object-file-convert.h\"\n #include \"object-file.h\"\n #include \"object-store.h\"\n@@ -361,6 +363,14 @@ struct object_directory *set_temporary_primary_odb(const char *dir, int will_des\n \treturn new_odb->next;\n }\n \n+static void free_object_directory(struct object_directory *odb)\n+{\n+\tfree(odb->path);\n+\todb_clear_loose_cache(odb);\n+\tloose_object_map_clear(&odb->loose_map);\n+\tfree(odb);\n+}\n+\n void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)\n {\n \tstruct object_directory *cur_odb = the_repository->objects->odb;\n@@ -970,3 +980,59 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect)\n \t\tdie(_(\"%s is not a valid '%s' object\"), oid_to_hex(oid),\n \t\t    type_name(expect));\n }\n+\n+struct raw_object_store *raw_object_store_new(void)\n+{\n+\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n+\n+\tmemset(o, 0, sizeof(*o));\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n+\tpthread_mutex_init(&o->replace_mutex, NULL);\n+\treturn o;\n+}\n+\n+static void free_object_directories(struct raw_object_store *o)\n+{\n+\twhile (o->odb) {\n+\t\tstruct object_directory *next;\n+\n+\t\tnext = o->odb->next;\n+\t\tfree_object_directory(o->odb);\n+\t\to->odb = next;\n+\t}\n+\tkh_destroy_odb_path_map(o->odb_by_path);\n+\to->odb_by_path = NULL;\n+}\n+\n+void raw_object_store_clear(struct raw_object_store *o)\n+{\n+\tFREE_AND_NULL(o->alternate_db);\n+\n+\toidmap_free(o->replace_map, 1);\n+\tFREE_AND_NULL(o->replace_map);\n+\tpthread_mutex_destroy(&o->replace_mutex);\n+\n+\tfree_commit_graph(o->commit_graph);\n+\to->commit_graph = NULL;\n+\to->commit_graph_attempted = 0;\n+\n+\tfree_object_directories(o);\n+\to->odb_tail = NULL;\n+\to->loaded_alternates = 0;\n+\n+\tINIT_LIST_HEAD(&o->packed_git_mru);\n+\tclose_object_store(o);\n+\n+\t/*\n+\t * `close_object_store()` only closes the packfiles, but doesn't free\n+\t * them. We thus have to do this manually.\n+\t */\n+\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n+\t\tnext = p->next;\n+\t\tfree(p);\n+\t}\n+\to->packed_git = NULL;\n+\n+\thashmap_clear(&o->pack_map);\n+}\ndiff --git a/object.c b/object.c\nindex 154525a4972..ccda798b75f 100644\n--- a/object.c\n+++ b/object.c\n@@ -6,16 +6,13 @@\n #include \"object.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store.h\"\n #include \"blob.h\"\n #include \"statinfo.h\"\n #include \"tree.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"alloc.h\"\n-#include \"packfile.h\"\n #include \"commit-graph.h\"\n-#include \"loose.h\"\n \n unsigned int get_max_object_index(const struct repository *repo)\n {\n@@ -567,70 +564,6 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo)\n \treturn o;\n }\n \n-struct raw_object_store *raw_object_store_new(void)\n-{\n-\tstruct raw_object_store *o = xmalloc(sizeof(*o));\n-\n-\tmemset(o, 0, sizeof(*o));\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\thashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);\n-\tpthread_mutex_init(&o->replace_mutex, NULL);\n-\treturn o;\n-}\n-\n-void free_object_directory(struct object_directory *odb)\n-{\n-\tfree(odb->path);\n-\todb_clear_loose_cache(odb);\n-\tloose_object_map_clear(&odb->loose_map);\n-\tfree(odb);\n-}\n-\n-static void free_object_directories(struct raw_object_store *o)\n-{\n-\twhile (o->odb) {\n-\t\tstruct object_directory *next;\n-\n-\t\tnext = o->odb->next;\n-\t\tfree_object_directory(o->odb);\n-\t\to->odb = next;\n-\t}\n-\tkh_destroy_odb_path_map(o->odb_by_path);\n-\to->odb_by_path = NULL;\n-}\n-\n-void raw_object_store_clear(struct raw_object_store *o)\n-{\n-\tFREE_AND_NULL(o->alternate_db);\n-\n-\toidmap_free(o->replace_map, 1);\n-\tFREE_AND_NULL(o->replace_map);\n-\tpthread_mutex_destroy(&o->replace_mutex);\n-\n-\tfree_commit_graph(o->commit_graph);\n-\to->commit_graph = NULL;\n-\to->commit_graph_attempted = 0;\n-\n-\tfree_object_directories(o);\n-\to->odb_tail = NULL;\n-\to->loaded_alternates = 0;\n-\n-\tINIT_LIST_HEAD(&o->packed_git_mru);\n-\tclose_object_store(o);\n-\n-\t/*\n-\t * `close_object_store()` only closes the packfiles, but doesn't free\n-\t * them. We thus have to do this manually.\n-\t */\n-\tfor (struct packed_git *p = o->packed_git, *next; p; p = next) {\n-\t\tnext = p->next;\n-\t\tfree(p);\n-\t}\n-\to->packed_git = NULL;\n-\n-\thashmap_clear(&o->pack_map);\n-}\n-\n void parsed_object_pool_reset_commit_grafts(struct parsed_object_pool *o)\n {\n \tfor (int i = 0; i < o->grafts_nr; i++) {\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516177","messageId":"20250415-pks-split-object-file-v3-9-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 09/10] object-store: remove global array of cached objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:22Z","receivedAt":"2025-04-15T09:38:28Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Cached objects are virtual objects that can be set up without writing\nanything into the object store directly, which is used by git-blame(1)\nto create fake commits for the working tree.\n\nThese cached objects are stored in a global variable, which is another\nroadblock for libification of the object subsystem. Refactor the code so\nthat we instead store the array as part of the raw object store.\n\nThis refactoring raises the question whether virtual objects should\nreally be specific to a single repository (or rather a single object\nstore). Hypothetical usecases might for example span across submodules,\nand here it may or may not be the right thing to provide virtual objects\nacross submodule boundaries.\n\nThe only existing usecase is git-blame(1) though, which does not know to\nblame across submodule boundaries in the first place. As such, storing\nthese objects both globally and per-repository would achieve the same\nresult right now. But arguably, if we learned to blame across submodule\nboundaries, we would likely want to create separate fare working tree\ncommits for each of the submodules so that the user can learn which\nworktree a specific uncommitted change belongs to. And even if we would\nwant to create the same fake commit for each of the submodules we could\ndo that when storing separate virtual objects per object store.\n\nWhile this is all rather hypothetical, the takeaway is that handling\nvirtual objects per-object store gives us more flexibility compared to\nstoring them globally. In a hypothetical future where we have achieved\nfull libification one might be able to handle unrelated repositories in\na single process, where the state of one repository should not have an\nimpact on the state of another repository. As such, storing these cached\nobjects per object store will enable more usecases and should lead to\nless surprising outcomes overall.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n blame.c           |  2 +-\n object-store-ll.h | 14 +++++++++++++-\n object-store.c    | 39 +++++++++++++++++++++++----------------\n 3 files changed, 37 insertions(+), 18 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 703dab43e78..b7c5bd692e6 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -277,7 +277,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,\n \tconvert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);\n \torigin->file.ptr = buf.buf;\n \torigin->file.size = buf.len;\n-\tpretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n+\tpretend_object_file(the_repository, buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);\n \n \t/*\n \t * Read the current index, replace the path entry with\ndiff --git a/object-store-ll.h b/object-store-ll.h\nindex 8bb0f33f9a8..bb5e8798a1b 100644\n--- a/object-store-ll.h\n+++ b/object-store-ll.h\n@@ -151,6 +151,8 @@ static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n }\n \n+struct cached_object_entry;\n+\n struct raw_object_store {\n \t/*\n \t * Set of all object directories; the main directory is first (and\n@@ -203,6 +205,15 @@ struct raw_object_store {\n \t\tunsigned flags;\n \t} kept_pack_cache;\n \n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n \t/*\n \t * A map of packfiles to packed_git structs for tracking which\n \t * packs have been loaded already.\n@@ -272,7 +283,8 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n  * object in persistent storage before writing any other new objects\n  * that reference it.\n  */\n-int pretend_object_file(void *, unsigned long, enum object_type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid);\n \n struct object_info {\ndiff --git a/object-store.c b/object-store.c\nindex 896d9ac3509..0f1dcc113ed 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -30,31 +30,31 @@\n  * to write them into the object store (e.g. a browse-only\n  * application).\n  */\n-static struct cached_object_entry {\n+struct cached_object_entry {\n \tstruct object_id oid;\n \tstruct cached_object {\n \t\tenum object_type type;\n \t\tconst void *buf;\n \t\tunsigned long size;\n \t} value;\n-} *cached_objects;\n-static int cached_object_nr, cached_object_alloc;\n+};\n \n-static const struct cached_object *find_cached_object(const struct object_id *oid)\n+static const struct cached_object *find_cached_object(struct raw_object_store *object_store,\n+\t\t\t\t\t\t      const struct object_id *oid)\n {\n \tstatic const struct cached_object empty_tree = {\n \t\t.type = OBJ_TREE,\n \t\t.buf = \"\",\n \t};\n-\tint i;\n-\tconst struct cached_object_entry *co = cached_objects;\n+\tconst struct cached_object_entry *co = object_store->cached_objects;\n \n-\tfor (i = 0; i < cached_object_nr; i++, co++) {\n+\tfor (size_t i = 0; i < object_store->cached_object_nr; i++, co++)\n \t\tif (oideq(&co->oid, oid))\n \t\t\treturn &co->value;\n-\t}\n-\tif (oideq(oid, the_hash_algo->empty_tree))\n+\n+\tif (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))\n \t\treturn &empty_tree;\n+\n \treturn NULL;\n }\n \n@@ -650,7 +650,7 @@ static int do_oid_object_info_extended(struct repository *r,\n \tif (!oi)\n \t\toi = &blank_oi;\n \n-\tco = find_cached_object(real);\n+\tco = find_cached_object(r->objects, real);\n \tif (co) {\n \t\tif (oi->typep)\n \t\t\t*(oi->typep) = co->type;\n@@ -853,18 +853,21 @@ int oid_object_info(struct repository *r,\n \treturn type;\n }\n \n-int pretend_object_file(void *buf, unsigned long len, enum object_type type,\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n \t\t\tstruct object_id *oid)\n {\n \tstruct cached_object_entry *co;\n \tchar *co_buf;\n \n-\thash_object_file(the_hash_algo, buf, len, type, oid);\n-\tif (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n-\t    find_cached_object(oid))\n+\thash_object_file(repo->hash_algo, buf, len, type, oid);\n+\tif (repo_has_object_file_with_flags(repo, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||\n+\t    find_cached_object(repo->objects, oid))\n \t\treturn 0;\n-\tALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);\n-\tco = &cached_objects[cached_object_nr++];\n+\n+\tALLOC_GROW(repo->objects->cached_objects,\n+\t\t   repo->objects->cached_object_nr + 1, repo->objects->cached_object_alloc);\n+\tco = &repo->objects->cached_objects[repo->objects->cached_object_nr++];\n \tco->value.size = len;\n \tco->value.type = type;\n \tco_buf = xmalloc(len);\n@@ -1021,6 +1024,10 @@ void raw_object_store_clear(struct raw_object_store *o)\n \to->odb_tail = NULL;\n \to->loaded_alternates = 0;\n \n+\tfor (size_t i = 0; i < o->cached_object_nr; i++)\n+\t\tfree((char *) o->cached_objects[i].value.buf);\n+\tFREE_AND_NULL(o->cached_objects);\n+\n \tINIT_LIST_HEAD(&o->packed_git_mru);\n \tclose_object_store(o);\n \n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516178","messageId":"20250415-pks-split-object-file-v3-10-6aa7db7ad7b0@pks.im","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"[PATCH v3 10/10] object-store: merge \"object-store-ll.h\" and \"object-store.h\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-15T09:38:23Z","receivedAt":"2025-04-15T09:38:30Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The \"object-store-ll.h\" header has been introduced to keep transitive\nheader dependendcies and compile times at bay. Now that we have created\na new \"object-store.c\" file though we can easily move the last remaining\nadditional bit of \"object-store.h\", the `odb_path_map`, out of the\nheader.\n\nDo so. As the \"object-store.h\" header is now equivalent to its low-level\nalternative we drop the latter and inline it into the former.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n apply.c                       |   2 +-\n archive-tar.c                 |   2 +-\n archive-zip.c                 |   2 +-\n archive.c                     |   2 +-\n attr.c                        |   2 +-\n bisect.c                      |   2 +-\n blame.c                       |   2 +-\n builtin/backfill.c            |   2 +-\n builtin/blame.c               |   2 +-\n builtin/cat-file.c            |   2 +-\n builtin/checkout.c            |   2 +-\n builtin/clone.c               |   2 +-\n builtin/commit-graph.c        |   2 +-\n builtin/commit-tree.c         |   2 +-\n builtin/count-objects.c       |   2 +-\n builtin/describe.c            |   2 +-\n builtin/difftool.c            |   2 +-\n builtin/fast-export.c         |   2 +-\n builtin/fast-import.c         |   2 +-\n builtin/fetch.c               |   2 +-\n builtin/fsck.c                |   2 +-\n builtin/gc.c                  |   2 +-\n builtin/grep.c                |   2 +-\n builtin/hash-object.c         |   2 +-\n builtin/index-pack.c          |   2 +-\n builtin/log.c                 |   2 +-\n builtin/ls-tree.c             |   2 +-\n builtin/merge-tree.c          |   2 +-\n builtin/mktag.c               |   2 +-\n builtin/mktree.c              |   2 +-\n builtin/multi-pack-index.c    |   2 +-\n builtin/notes.c               |   2 +-\n builtin/pack-objects.c        |   2 +-\n builtin/pack-redundant.c      |   2 +-\n builtin/prune.c               |   2 +-\n builtin/receive-pack.c        |   2 +-\n builtin/remote.c              |   2 +-\n builtin/repack.c              |   2 +-\n builtin/replace.c             |   2 +-\n builtin/rev-list.c            |   2 +-\n builtin/show-ref.c            |   2 +-\n builtin/submodule--helper.c   |   2 +-\n builtin/tag.c                 |   2 +-\n builtin/unpack-file.c         |   2 +-\n builtin/unpack-objects.c      |   2 +-\n bulk-checkin.c                |   2 +-\n bundle-uri.c                  |   2 +-\n bundle.c                      |   2 +-\n cache-tree.c                  |   2 +-\n combine-diff.c                |   2 +-\n commit-graph.c                |   2 +-\n commit-graph.h                |   2 +-\n commit.c                      |   2 +-\n config.c                      |   2 +-\n connected.c                   |   2 +-\n convert.c                     |   2 +-\n diagnose.c                    |   2 +-\n diff.c                        |   2 +-\n diffcore-rename.c             |   2 +-\n dir.c                         |   2 +-\n entry.c                       |   2 +-\n fetch-pack.c                  |   2 +-\n fmt-merge-msg.c               |   2 +-\n fsck.c                        |   2 +-\n grep.c                        |   2 +-\n http-backend.c                |   2 +-\n http-push.c                   |   2 +-\n http-walker.c                 |   2 +-\n http.c                        |   2 +-\n list-objects-filter.c         |   2 +-\n list-objects.c                |   2 +-\n log-tree.c                    |   2 +-\n mailmap.c                     |   2 +-\n merge-blobs.c                 |   2 +-\n merge-recursive.c             |   2 +-\n notes-merge.c                 |   2 +-\n object-file.c                 |   1 +\n object-name.c                 |   2 +-\n object-store-ll.h             | 517 ------------------------------------------\n object-store.c                |   5 +\n object-store.h                | 516 ++++++++++++++++++++++++++++++++++++++++-\n oss-fuzz/fuzz-pack-idx.c      |   2 +-\n pack-bitmap-write.c           |   2 +-\n pack-bitmap.c                 |   2 +-\n pack-check.c                  |   2 +-\n pack-mtimes.c                 |   2 +-\n pack-objects.h                |   2 +-\n pack-revindex.c               |   2 +-\n packfile.c                    |   2 +-\n path.c                        |   2 +-\n promisor-remote.c             |   2 +-\n protocol-caps.c               |   2 +-\n prune-packed.c                |   2 +-\n reachable.c                   |   2 +-\n read-cache.c                  |   2 +-\n ref-filter.c                  |   2 +-\n reflog.c                      |   2 +-\n refs.c                        |   2 +-\n remote.c                      |   2 +-\n replace-object.c              |   2 +-\n replace-object.h              |   2 +-\n repository.c                  |   2 +-\n rerere.c                      |   2 +-\n revision.c                    |   2 +-\n send-pack.c                   |   2 +-\n sequencer.c                   |   2 +-\n server-info.c                 |   2 +-\n shallow.c                     |   2 +-\n streaming.c                   |   2 +-\n submodule-config.c            |   2 +-\n submodule.c                   |   2 +-\n t/helper/test-pack-mtimes.c   |   2 +-\n t/helper/test-partial-clone.c |   2 +-\n t/helper/test-read-graph.c    |   2 +-\n t/helper/test-read-midx.c     |   2 +-\n t/helper/test-ref-store.c     |   2 +-\n tag.c                         |   2 +-\n tmp-objdir.c                  |   2 +-\n tree-walk.c                   |   2 +-\n tree.c                        |   2 +-\n unpack-trees.c                |   2 +-\n upload-pack.c                 |   2 +-\n walker.c                      |   2 +-\n xdiff-interface.c             |   2 +-\n 124 files changed, 637 insertions(+), 642 deletions(-)\n\ndiff --git a/apply.c b/apply.c\nindex f274a379487..2b6f4d0af87 100644\n--- a/apply.c\n+++ b/apply.c\n@@ -14,7 +14,7 @@\n #include \"abspath.h\"\n #include \"base85.h\"\n #include \"config.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"delta.h\"\n #include \"diff.h\"\n #include \"dir.h\"\ndiff --git a/archive-tar.c b/archive-tar.c\nindex 0edf13fba75..282b48196f9 100644\n--- a/archive-tar.c\n+++ b/archive-tar.c\n@@ -11,7 +11,7 @@\n #include \"hex.h\"\n #include \"tar.h\"\n #include \"archive.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"streaming.h\"\n #include \"run-command.h\"\ndiff --git a/archive-zip.c b/archive-zip.c\nindex 9f32730181b..405da6f3d83 100644\n--- a/archive-zip.c\n+++ b/archive-zip.c\n@@ -12,7 +12,7 @@\n #include \"hex.h\"\n #include \"streaming.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"userdiff.h\"\n #include \"write-or-die.h\"\ndiff --git a/archive.c b/archive.c\nindex c95e3981524..014c312178c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -14,7 +14,7 @@\n #include \"pretty.h\"\n #include \"setup.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\ndiff --git a/attr.c b/attr.c\nindex 0bd2750528f..86b6109fc4e 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -22,7 +22,7 @@\n #include \"read-cache-ll.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/bisect.c b/bisect.c\nindex 269a98bf978..a327468c75b 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -20,7 +20,7 @@\n #include \"commit-slab.h\"\n #include \"commit-reach.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"dir.h\"\n \ndiff --git a/blame.c b/blame.c\nindex b7c5bd692e6..57daa45e899 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"refs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"cache-tree.h\"\n #include \"mergesort.h\"\n #include \"commit.h\"\ndiff --git a/builtin/backfill.c b/builtin/backfill.c\nindex 33e1ea2f84f..aaa104bc91d 100644\n--- a/builtin/backfill.c\n+++ b/builtin/backfill.c\n@@ -13,7 +13,7 @@\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"promisor-remote.h\"\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex c470654c7ec..4e156bfd19d 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -28,7 +28,7 @@\n #include \"line-log.h\"\n #include \"progress.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"blame.h\"\n #include \"refs.h\"\ndiff --git a/builtin/cat-file.c b/builtin/cat-file.c\nindex b13561cf73b..c870fde260a 100644\n--- a/builtin/cat-file.c\n+++ b/builtin/cat-file.c\n@@ -22,7 +22,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"promisor-remote.h\"\n #include \"mailmap.h\"\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3e68623838a..8136962e2b3 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -20,7 +20,7 @@\n #include \"merge-recursive.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"path.h\"\n #include \"preload-index.h\"\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 31f2198c1b3..f64be7e1e15 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -25,7 +25,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\ndiff --git a/builtin/commit-graph.c b/builtin/commit-graph.c\nindex 8ca75262c59..be06d0a811b 100644\n--- a/builtin/commit-graph.c\n+++ b/builtin/commit-graph.c\n@@ -6,7 +6,7 @@\n #include \"hex.h\"\n #include \"parse-options.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"progress.h\"\n #include \"replace-object.h\"\n #include \"strbuf.h\"\ndiff --git a/builtin/commit-tree.c b/builtin/commit-tree.c\nindex 38457600a4e..6f9975e7a88 100644\n--- a/builtin/commit-tree.c\n+++ b/builtin/commit-tree.c\n@@ -9,7 +9,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n #include \"commit.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 1e89148ed74..0bb5360b2f2 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -12,7 +12,7 @@\n #include \"parse-options.h\"\n #include \"quote.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static unsigned long garbage;\n static off_t size_garbage;\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 23df333fd04..0f87fbceef3 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -19,7 +19,7 @@\n #include \"setup.h\"\n #include \"strvec.h\"\n #include \"run-command.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects.h\"\n #include \"commit-slab.h\"\n #include \"wildmatch.h\"\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex 8292aedaaf0..f17a55b3cf9 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -30,7 +30,7 @@\n #include \"strbuf.h\"\n #include \"lockfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"entry.h\"\n #include \"setup.h\"\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 170126d41ac..afacd228b5d 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex e4523cc6f1b..c1e198f4e34 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -24,7 +24,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"mem-pool.h\"\n #include \"commit-reach.h\"\n #include \"khash.h\"\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 02af5054690..b52a32a5e0d 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -14,7 +14,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"oid-array.h\"\n #include \"commit.h\"\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 92312b59591..6cac28356ce 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -17,7 +17,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex dae1e545514..4247e0e7f7c 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -28,7 +28,7 @@\n #include \"commit.h\"\n #include \"commit-graph.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\n #include \"path.h\"\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 283d64cab80..bcfbe5be5ba 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -26,7 +26,7 @@\n #include \"submodule-config.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pager.h\"\n #include \"path.h\"\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex e7c0d6afdef..cd53fa3bde8 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -11,7 +11,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"blob.h\"\n #include \"quote.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex de127c0ff13..60a8ee05dbc 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -21,7 +21,7 @@\n #include \"packfile.h\"\n #include \"pack-revindex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"path.h\"\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 516c9ec8400..b450cd3bde8 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -15,7 +15,7 @@\n #include \"hex.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"color.h\"\n #include \"commit.h\"\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8542b5d53e4..8aafc30ca48 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -10,7 +10,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tree.h\"\n #include \"path.h\"\n #include \"quote.h\"\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 3ec7127b3a6..4aafa73c615 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -10,7 +10,7 @@\n #include \"commit-reach.h\"\n #include \"merge-ort.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 6e188dce50a..7ac11c46d53 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -6,7 +6,7 @@\n #include \"strbuf.h\"\n #include \"replace-object.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"fsck.h\"\n #include \"config.h\"\n \ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 0644f951161..7ffe6eefd8a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -12,7 +12,7 @@\n #include \"tree.h\"\n #include \"parse-options.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct treeent {\n \tunsigned mode;\ndiff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\nindex 2a938466f53..d98410ca6c6 100644\n--- a/builtin/multi-pack-index.c\n+++ b/builtin/multi-pack-index.c\n@@ -7,7 +7,7 @@\n #include \"midx.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 0dbc233752d..a3f433ca4c0 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -16,7 +16,7 @@\n #include \"notes.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n \n #include \"pretty.h\"\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 163aab547fe..488c80f2cf3 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -32,7 +32,7 @@\n #include \"list.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"dir.h\"\n #include \"midx.h\"\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex 3febe732f8e..5d1fc781761 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -13,7 +13,7 @@\n #include \"hex.h\"\n \n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n \n #define BLKSIZE 512\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex 8f52da8bd66..e930caa0c0a 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -17,7 +17,7 @@\n #include \"replace-object.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"shallow.h\"\n \n static const char * const prune_usage[] = {\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex ee51bd76f60..be314879e82 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -33,7 +33,7 @@\n #include \"packfile.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"protocol.h\"\n #include \"commit-reach.h\"\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 1b7aad88380..59481b3a82c 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"rebase.h\"\n #include \"refs.h\"\n #include \"refspec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strvec.h\"\n #include \"commit-reach.h\"\n #include \"progress.h\"\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex f3330ade7b8..1fd2874324a 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -17,7 +17,7 @@\n #include \"midx.h\"\n #include \"packfile.h\"\n #include \"prune-packed.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"shallow.h\"\n #include \"pack.h\"\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 2b4fc9a68b3..48c7c6a2d56 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"tag.h\"\n #include \"wildmatch.h\"\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex bb26bee0d45..0170d79b62f 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -14,7 +14,7 @@\n #include \"object.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"log-tree.h\"\n #include \"graph.h\"\ndiff --git a/builtin/show-ref.c b/builtin/show-ref.c\nindex 285cd3e4338..f81209f23c3 100644\n--- a/builtin/show-ref.c\n+++ b/builtin/show-ref.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"refs/refs-internal.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"string-list.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex cc001d0b4cd..53da2116ddf 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -28,7 +28,7 @@\n #include \"diff.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"advice.h\"\n #include \"branch.h\"\n #include \"list-objects-filter-options.h\"\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 536a01ff3ae..e6b372cebf5 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -19,7 +19,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"tag.h\"\n #include \"parse-options.h\"\ndiff --git a/builtin/unpack-file.c b/builtin/unpack-file.c\nindex b19e5cabd03..e33acfc4ee4 100644\n--- a/builtin/unpack-file.c\n+++ b/builtin/unpack-file.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static char *create_temp_file(struct object_id *oid)\n {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 4078eab9252..661be789f13 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -9,7 +9,7 @@\n #include \"git-zlib.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"delta.h\"\n #include \"pack.h\"\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 309201a76a6..684d49023af 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -18,7 +18,7 @@\n #include \"tmp-objdir.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int odb_transaction_nesting;\n \ndiff --git a/bundle-uri.c b/bundle-uri.c\nindex 744257c49c1..96d2ba726d9 100644\n--- a/bundle-uri.c\n+++ b/bundle-uri.c\n@@ -14,7 +14,7 @@\n #include \"fetch-pack.h\"\n #include \"remote.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static struct {\n \tenum bundle_list_heuristic heuristic;\ndiff --git a/bundle.c b/bundle.c\nindex d7ad6908433..d661c4ec214 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -7,7 +7,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 4c8167ea927..c0e1e9ee1d4 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -10,7 +10,7 @@\n #include \"cache-tree.h\"\n #include \"bulk-checkin.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"replace-object.h\"\n #include \"repository.h\"\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 553bf59fed6..dfae9f7995d 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -2,7 +2,7 @@\n #define DISABLE_SIGN_COMPARE_WARNINGS\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"convert.h\"\n #include \"diff.h\"\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 9621c454972..6394752b0b0 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -13,7 +13,7 @@\n #include \"refs.h\"\n #include \"hash-lookup.h\"\n #include \"commit-graph.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"path.h\"\n #include \"alloc.h\"\ndiff --git a/commit-graph.h b/commit-graph.h\nindex 67819401954..13f662827d4 100644\n--- a/commit-graph.h\n+++ b/commit-graph.h\n@@ -1,7 +1,7 @@\n #ifndef COMMIT_GRAPH_H\n #define COMMIT_GRAPH_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n \n #define GIT_TEST_COMMIT_GRAPH \"GIT_TEST_COMMIT_GRAPH\"\ndiff --git a/commit.c b/commit.c\nindex fbf4f8e87fd..00842678bd2 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -9,7 +9,7 @@\n #include \"hex.h\"\n #include \"repository.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"utf8.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/config.c b/config.c\nindex accb47e2d18..b18b5617fcd 100644\n--- a/config.c\n+++ b/config.c\n@@ -31,7 +31,7 @@\n #include \"hashmap.h\"\n #include \"string-list.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pager.h\"\n #include \"path.h\"\n #include \"utf8.h\"\ndiff --git a/connected.c b/connected.c\nindex 3099da84f33..4415388beba 100644\n--- a/connected.c\n+++ b/connected.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n #include \"connected.h\"\ndiff --git a/convert.c b/convert.c\nindex 9cc0ca20ca0..8783e17941f 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -8,7 +8,7 @@\n #include \"copy.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"attr.h\"\n #include \"run-command.h\"\n #include \"quote.h\"\ndiff --git a/diagnose.c b/diagnose.c\nindex bd485effea2..b1be74be983 100644\n--- a/diagnose.c\n+++ b/diagnose.c\n@@ -7,7 +7,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"strvec.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"parse-options.h\"\n #include \"repository.h\"\ndiff --git a/diff.c b/diff.c\nindex 3bcf5028831..91d5b962b57 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -23,7 +23,7 @@\n #include \"color.h\"\n #include \"run-command.h\"\n #include \"utf8.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"userdiff.h\"\n #include \"submodule.h\"\n #include \"hashmap.h\"\ndiff --git a/diffcore-rename.c b/diffcore-rename.c\nindex 5002e896aad..787a2cef5f4 100644\n--- a/diffcore-rename.c\n+++ b/diffcore-rename.c\n@@ -8,7 +8,7 @@\n #include \"git-compat-util.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"hashmap.h\"\n #include \"mem-pool.h\"\n #include \"oid-array.h\"\ndiff --git a/dir.c b/dir.c\nindex 49008739b9b..5c4675b4ac4 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -17,7 +17,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"name-hash.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/entry.c b/entry.c\nindex 81b321e53d1..f36ec5ad242 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -1,7 +1,7 @@\n #define USE_THE_REPOSITORY_VARIABLE\n \n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 1ed5e11dd56..210dc30d50f 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -24,7 +24,7 @@\n #include \"oid-array.h\"\n #include \"oidset.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"connected.h\"\n #include \"fetch-negotiator.h\"\ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex 5b63c3b088a..501b5acdd44 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -6,7 +6,7 @@\n #include \"environment.h\"\n #include \"refs.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"diff.h\"\n #include \"diff-merges.h\"\n #include \"hex.h\"\ndiff --git a/fsck.c b/fsck.c\nindex 9fc4c25ffd5..8dc8472ceb3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"dir.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"object.h\"\ndiff --git a/grep.c b/grep.c\nindex 9284b5741f5..f8d535182c3 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"grep.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pretty.h\"\n #include \"userdiff.h\"\n #include \"xdiff-interface.h\"\ndiff --git a/http-backend.c b/http-backend.c\nindex 50b2858fad6..0c575aa88aa 100644\n--- a/http-backend.c\n+++ b/http-backend.c\n@@ -18,7 +18,7 @@\n #include \"url.h\"\n #include \"strvec.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"protocol.h\"\n #include \"date.h\"\n #include \"write-or-die.h\"\ndiff --git a/http-push.c b/http-push.c\nindex 806eb67cf1b..32e37565f4e 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -20,7 +20,7 @@\n #include \"url.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n \n #ifdef EXPAT_NEEDS_XMLPARSE_H\ndiff --git a/http-walker.c b/http-walker.c\nindex 7918ddc0968..882cae19c24 100644\n--- a/http-walker.c\n+++ b/http-walker.c\n@@ -9,7 +9,7 @@\n #include \"list.h\"\n #include \"transport.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct alt_base {\n \tchar *base;\ndiff --git a/http.c b/http.c\nindex 0c9a872809f..bc18ff83c4b 100644\n--- a/http.c\n+++ b/http.c\n@@ -19,7 +19,7 @@\n #include \"packfile.h\"\n #include \"string-list.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"tempfile.h\"\n \n static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);\ndiff --git a/list-objects-filter.c b/list-objects-filter.c\nindex dc598a081bb..7765761b3c6 100644\n--- a/list-objects-filter.c\n+++ b/list-objects-filter.c\n@@ -12,7 +12,7 @@\n #include \"oidmap.h\"\n #include \"oidset.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n /* Remember to update object flag allocation in object.h */\n /*\ndiff --git a/list-objects.c b/list-objects.c\nindex 943e62e868f..1e5512e1318 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -14,7 +14,7 @@\n #include \"list-objects-filter.h\"\n #include \"list-objects-filter-options.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace.h\"\n #include \"environment.h\"\n \ndiff --git a/log-tree.c b/log-tree.c\nindex 5dd1b63076f..a4d4ab59ca0 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -9,7 +9,7 @@\n #include \"environment.h\"\n #include \"hex.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"tmp-objdir.h\"\n #include \"commit.h\"\ndiff --git a/mailmap.c b/mailmap.c\nindex f35d20ed7fd..9e2642a043b 100644\n--- a/mailmap.c\n+++ b/mailmap.c\n@@ -6,7 +6,7 @@\n #include \"string-list.h\"\n #include \"mailmap.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n char *git_mailmap_file;\ndiff --git a/merge-blobs.c b/merge-blobs.c\nindex 0ad0390fea5..53f36dbc175 100644\n--- a/merge-blobs.c\n+++ b/merge-blobs.c\n@@ -4,7 +4,7 @@\n #include \"merge-ll.h\"\n #include \"blob.h\"\n #include \"merge-blobs.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)\n {\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f71490517e1..b852f467674 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -27,7 +27,7 @@\n #include \"name-hash.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"revision.h\"\ndiff --git a/notes-merge.c b/notes-merge.c\nindex 520b92942cd..dae8e6a281a 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -8,7 +8,7 @@\n #include \"refs.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"diff.h\"\ndiff --git a/object-file.c b/object-file.c\nindex 6084d603136..450e1cae346 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -13,6 +13,7 @@\n #include \"git-compat-util.h\"\n #include \"bulk-checkin.h\"\n #include \"convert.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"fsck.h\"\n #include \"gettext.h\"\ndiff --git a/object-name.c b/object-name.c\nindex 91f731373a1..2c751a5352a 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -19,7 +19,7 @@\n #include \"oidtree.h\"\n #include \"packfile.h\"\n #include \"pretty.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"read-cache-ll.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/object-store-ll.h b/object-store-ll.h\ndeleted file mode 100644\nindex bb5e8798a1b..00000000000\n--- a/object-store-ll.h\n+++ /dev/null\n@@ -1,517 +0,0 @@\n-#ifndef OBJECT_STORE_LL_H\n-#define OBJECT_STORE_LL_H\n-\n-#include \"hashmap.h\"\n-#include \"object.h\"\n-#include \"list.h\"\n-#include \"thread-utils.h\"\n-#include \"oidset.h\"\n-\n-struct oidmap;\n-struct oidtree;\n-struct strbuf;\n-struct repository;\n-\n-struct object_directory {\n-\tstruct object_directory *next;\n-\n-\t/*\n-\t * Used to store the results of readdir(3) calls when we are OK\n-\t * sacrificing accuracy due to races for speed. That includes\n-\t * object existence with OBJECT_INFO_QUICK, as well as\n-\t * our search for unique abbreviated hashes. Don't use it for tasks\n-\t * requiring greater accuracy!\n-\t *\n-\t * Be sure to call odb_load_loose_cache() before using.\n-\t */\n-\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n-\tstruct oidtree *loose_objects_cache;\n-\n-\t/* Map between object IDs for loose objects. */\n-\tstruct loose_object_map *loose_map;\n-\n-\t/*\n-\t * This is a temporary object store created by the tmp_objdir\n-\t * facility. Disable ref updates since the objects in the store\n-\t * might be discarded on rollback.\n-\t */\n-\tint disable_ref_updates;\n-\n-\t/*\n-\t * This object store is ephemeral, so there is no need to fsync.\n-\t */\n-\tint will_destroy;\n-\n-\t/*\n-\t * Path to the alternative object store. If this is a relative path,\n-\t * it is relative to the current working directory.\n-\t */\n-\tchar *path;\n-};\n-\n-void prepare_alt_odb(struct repository *r);\n-int has_alt_odb(struct repository *r);\n-char *compute_alternate_path(const char *path, struct strbuf *err);\n-struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n-typedef int alt_odb_fn(struct object_directory *, void *);\n-int foreach_alt_odb(alt_odb_fn, void*);\n-typedef void alternate_ref_fn(const struct object_id *oid, void *);\n-void for_each_alternate_ref(alternate_ref_fn, void *);\n-\n-/*\n- * Add the directory to the on-disk alternates file; the new entry will also\n- * take effect in the current process.\n- */\n-void add_to_alternates_file(const char *dir);\n-\n-/*\n- * Add the directory to the in-memory list of alternates (along with any\n- * recursive alternates it points to), but do not modify the on-disk alternates\n- * file.\n- */\n-void add_to_alternates_memory(const char *dir);\n-\n-/*\n- * Replace the current writable object directory with the specified temporary\n- * object directory; returns the former primary object directory.\n- */\n-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n-\n-/*\n- * Restore a previous ODB replaced by set_temporary_main_odb.\n- */\n-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n-\n-/*\n- * Populate and return the loose object cache array corresponding to the\n- * given object ID.\n- */\n-struct oidtree *odb_loose_cache(struct object_directory *odb,\n-\t\t\t\t  const struct object_id *oid);\n-\n-/* Empty the loose object cache for the specified object directory. */\n-void odb_clear_loose_cache(struct object_directory *odb);\n-\n-struct packed_git {\n-\tstruct hashmap_entry packmap_ent;\n-\tstruct packed_git *next;\n-\tstruct list_head mru;\n-\tstruct pack_window *windows;\n-\toff_t pack_size;\n-\tconst void *index_data;\n-\tsize_t index_size;\n-\tuint32_t num_objects;\n-\tsize_t crc_offset;\n-\tstruct oidset bad_objects;\n-\tint index_version;\n-\ttime_t mtime;\n-\tint pack_fd;\n-\tint index;              /* for builtin/pack-objects.c */\n-\tunsigned pack_local:1,\n-\t\t pack_keep:1,\n-\t\t pack_keep_in_core:1,\n-\t\t freshened:1,\n-\t\t do_not_close:1,\n-\t\t pack_promisor:1,\n-\t\t multi_pack_index:1,\n-\t\t is_cruft:1;\n-\tunsigned char hash[GIT_MAX_RAWSZ];\n-\tstruct revindex_entry *revindex;\n-\tconst uint32_t *revindex_data;\n-\tconst uint32_t *revindex_map;\n-\tsize_t revindex_size;\n-\t/*\n-\t * mtimes_map points at the beginning of the memory mapped region of\n-\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n-\t * of that .mtimes file\n-\t */\n-\tconst uint32_t *mtimes_map;\n-\tsize_t mtimes_size;\n-\n-\t/* repo denotes the repository this packfile belongs to */\n-\tstruct repository *repo;\n-\n-\t/* something like \".git/objects/pack/xxxxx.pack\" */\n-\tchar pack_name[FLEX_ARRAY]; /* more */\n-};\n-\n-struct multi_pack_index;\n-\n-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n-\t\t\t\t     const struct hashmap_entry *entry,\n-\t\t\t\t     const struct hashmap_entry *entry2,\n-\t\t\t\t     const void *keydata)\n-{\n-\tconst char *key = keydata;\n-\tconst struct packed_git *pg1, *pg2;\n-\n-\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n-\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n-\n-\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n-}\n-\n-struct cached_object_entry;\n-\n-struct raw_object_store {\n-\t/*\n-\t * Set of all object directories; the main directory is first (and\n-\t * cannot be NULL after initialization). Subsequent directories are\n-\t * alternates.\n-\t */\n-\tstruct object_directory *odb;\n-\tstruct object_directory **odb_tail;\n-\tstruct kh_odb_path_map *odb_by_path;\n-\n-\tint loaded_alternates;\n-\n-\t/*\n-\t * A list of alternate object directories loaded from the environment;\n-\t * this should not generally need to be accessed directly, but will\n-\t * populate the \"odb\" list when prepare_alt_odb() is run.\n-\t */\n-\tchar *alternate_db;\n-\n-\t/*\n-\t * Objects that should be substituted by other objects\n-\t * (see git-replace(1)).\n-\t */\n-\tstruct oidmap *replace_map;\n-\tunsigned replace_map_initialized : 1;\n-\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n-\n-\tstruct commit_graph *commit_graph;\n-\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c and midx.c\n-\t */\n-\tstruct multi_pack_index *multi_pack_index;\n-\n-\t/*\n-\t * private data\n-\t *\n-\t * should only be accessed directly by packfile.c\n-\t */\n-\n-\tstruct packed_git *packed_git;\n-\t/* A most-recently-used ordered version of the packed_git list. */\n-\tstruct list_head packed_git_mru;\n-\n-\tstruct {\n-\t\tstruct packed_git **packs;\n-\t\tunsigned flags;\n-\t} kept_pack_cache;\n-\n-\t/*\n-\t * This is meant to hold a *small* number of objects that you would\n-\t * want repo_read_object_file() to be able to return, but yet you do not want\n-\t * to write them into the object store (e.g. a browse-only\n-\t * application).\n-\t */\n-\tstruct cached_object_entry *cached_objects;\n-\tsize_t cached_object_nr, cached_object_alloc;\n-\n-\t/*\n-\t * A map of packfiles to packed_git structs for tracking which\n-\t * packs have been loaded already.\n-\t */\n-\tstruct hashmap pack_map;\n-\n-\t/*\n-\t * A fast, rough count of the number of objects in the repository.\n-\t * These two fields are not meant for direct access. Use\n-\t * repo_approximate_object_count() instead.\n-\t */\n-\tunsigned long approximate_object_count;\n-\tunsigned approximate_object_count_valid : 1;\n-\n-\t/*\n-\t * Whether packed_git has already been populated with this repository's\n-\t * packs.\n-\t */\n-\tunsigned packed_git_initialized : 1;\n-};\n-\n-struct raw_object_store *raw_object_store_new(void);\n-void raw_object_store_clear(struct raw_object_store *o);\n-\n-/*\n- * Create a temporary file rooted in the object database directory, or\n- * die on failure. The filename is taken from \"pattern\", which should have the\n- * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n- * \"template\" buffer. Returns the open descriptor.\n- */\n-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n-\n-/*\n- * Create a pack .keep file named \"name\" (which should generally be the output\n- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n- * error.\n- */\n-int odb_pack_keep(const char *name);\n-\n-/*\n- * Put in `buf` the name of the file in the local object database that\n- * would be used to store a loose object with the specified oid.\n- */\n-const char *loose_object_path(struct repository *r, struct strbuf *buf,\n-\t\t\t      const struct object_id *oid);\n-\n-void *map_loose_object(struct repository *r, const struct object_id *oid,\n-\t\t       unsigned long *size);\n-\n-void *repo_read_object_file(struct repository *r,\n-\t\t\t    const struct object_id *oid,\n-\t\t\t    enum object_type *type,\n-\t\t\t    unsigned long *size);\n-\n-/* Read and unpack an object file into memory, write memory to an object file */\n-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n-\n-void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n-\t\t      unsigned long len, enum object_type type,\n-\t\t      struct object_id *oid);\n-\n-/*\n- * Add an object file to the in-memory object store, without writing it\n- * to disk.\n- *\n- * Callers are responsible for calling write_object_file to record the\n- * object in persistent storage before writing any other new objects\n- * that reference it.\n- */\n-int pretend_object_file(struct repository *repo,\n-\t\t\tvoid *buf, unsigned long len, enum object_type type,\n-\t\t\tstruct object_id *oid);\n-\n-struct object_info {\n-\t/* Request */\n-\tenum object_type *typep;\n-\tunsigned long *sizep;\n-\toff_t *disk_sizep;\n-\tstruct object_id *delta_base_oid;\n-\tstruct strbuf *type_name;\n-\tvoid **contentp;\n-\n-\t/* Response */\n-\tenum {\n-\t\tOI_CACHED,\n-\t\tOI_LOOSE,\n-\t\tOI_PACKED,\n-\t\tOI_DBCACHED\n-\t} whence;\n-\tunion {\n-\t\t/*\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } cached;\n-\t\t * struct {\n-\t\t * \t... Nothing to expose in this case\n-\t\t * } loose;\n-\t\t */\n-\t\tstruct {\n-\t\t\tstruct packed_git *pack;\n-\t\t\toff_t offset;\n-\t\t\tunsigned int is_delta;\n-\t\t} packed;\n-\t} u;\n-};\n-\n-/*\n- * Initializer for a \"struct object_info\" that wants no items. You may\n- * also memset() the memory to all-zeroes.\n- */\n-#define OBJECT_INFO_INIT { 0 }\n-\n-/* Invoke lookup_replace_object() on the given hash */\n-#define OBJECT_INFO_LOOKUP_REPLACE 1\n-/* Allow reading from a loose object file of unknown/bogus type */\n-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n-/* Do not retry packed storage after checking packed and loose storage */\n-#define OBJECT_INFO_QUICK 8\n-/*\n- * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n- * nonzero).\n- */\n-#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n-/*\n- * This is meant for bulk prefetching of missing blobs in a partial\n- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n- */\n-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n-\n-/* Die if object corruption (not just an object being missing) was detected. */\n-#define OBJECT_INFO_DIE_IF_CORRUPT 32\n-\n-int oid_object_info_extended(struct repository *r,\n-\t\t\t     const struct object_id *,\n-\t\t\t     struct object_info *, unsigned flags);\n-\n-/* Retry packed storage after checking packed and loose storage */\n-#define HAS_OBJECT_RECHECK_PACKED 1\n-\n-/*\n- * Returns 1 if the object exists. This function will not lazily fetch objects\n- * in a partial clone.\n- */\n-int has_object(struct repository *r, const struct object_id *oid,\n-\t       unsigned flags);\n-\n-/*\n- * These macros and functions are deprecated. If checking existence for an\n- * object that is likely to be missing and/or whose absence is relatively\n- * inconsequential (or is consequential but the caller is prepared to handle\n- * it), use has_object(), which has better defaults (no lazy fetch in a partial\n- * clone and no rechecking of packed storage). In the unlikely event that a\n- * caller needs to assert existence of an object that it fully expects to\n- * exist, and wants to trigger a lazy fetch in a partial clone, use\n- * oid_object_info_extended() with a NULL struct object_info.\n- *\n- * These functions can be removed once all callers have migrated to\n- * has_object() and/or oid_object_info_extended().\n- */\n-int repo_has_object_file(struct repository *r, const struct object_id *oid);\n-int repo_has_object_file_with_flags(struct repository *r,\n-\t\t\t\t    const struct object_id *oid, int flags);\n-\n-void assert_oid_type(const struct object_id *oid, enum object_type expect);\n-\n-/*\n- * Enabling the object read lock allows multiple threads to safely call the\n- * following functions in parallel: repo_read_object_file(),\n- * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n- *\n- * obj_read_lock() and obj_read_unlock() may also be used to protect other\n- * section which cannot execute in parallel with object reading. Since the used\n- * lock is a recursive mutex, these sections can even contain calls to object\n- * reading functions. However, beware that in these cases zlib inflation won't\n- * be performed in parallel, losing performance.\n- *\n- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n- * any of its callees end up calling it, this recursive call won't benefit from\n- * parallel inflation.\n- */\n-void enable_obj_read_lock(void);\n-void disable_obj_read_lock(void);\n-\n-extern int obj_read_use_lock;\n-extern pthread_mutex_t obj_read_mutex;\n-\n-static inline void obj_read_lock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_lock(&obj_read_mutex);\n-}\n-\n-static inline void obj_read_unlock(void)\n-{\n-\tif(obj_read_use_lock)\n-\t\tpthread_mutex_unlock(&obj_read_mutex);\n-}\n-\n-/*\n- * Iterate over the files in the loose-object parts of the object\n- * directory \"path\", triggering the following callbacks:\n- *\n- *  - loose_object is called for each loose object we find.\n- *\n- *  - loose_cruft is called for any files that do not appear to be\n- *    loose objects. Note that we only look in the loose object\n- *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n- *    \"objects/foobar\" as cruft.\n- *\n- *  - loose_subdir is called for each top-level hashed subdirectory\n- *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n- *    after the objects in the directory are processed.\n- *\n- * Any callback that is NULL will be ignored. Callbacks returning non-zero\n- * will end the iteration.\n- *\n- * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n- * scratch buffer, but restored to its original contents before\n- * the function returns.\n- */\n-typedef int each_loose_object_fn(const struct object_id *oid,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-typedef int each_loose_cruft_fn(const char *basename,\n-\t\t\t\tconst char *path,\n-\t\t\t\tvoid *data);\n-typedef int each_loose_subdir_fn(unsigned int nr,\n-\t\t\t\t const char *path,\n-\t\t\t\t void *data);\n-int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n-\t\t\t\tstruct strbuf *path,\n-\t\t\t\teach_loose_object_fn obj_cb,\n-\t\t\t\teach_loose_cruft_fn cruft_cb,\n-\t\t\t\teach_loose_subdir_fn subdir_cb,\n-\t\t\t\tvoid *data);\n-int for_each_loose_file_in_objdir(const char *path,\n-\t\t\t\t  each_loose_object_fn obj_cb,\n-\t\t\t\t  each_loose_cruft_fn cruft_cb,\n-\t\t\t\t  each_loose_subdir_fn subdir_cb,\n-\t\t\t\t  void *data);\n-int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n-\t\t\t\t      each_loose_object_fn obj_cb,\n-\t\t\t\t      each_loose_cruft_fn cruft_cb,\n-\t\t\t\t      each_loose_subdir_fn subdir_cb,\n-\t\t\t\t      void *data);\n-\n-/* Flags for for_each_*_object() below. */\n-enum for_each_object_flags {\n-\t/* Iterate only over local objects, not alternates. */\n-\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n-\n-\t/* Only iterate over packs obtained from the promisor remote. */\n-\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n-\n-\t/*\n-\t * Visit objects within a pack in packfile order rather than .idx order\n-\t */\n-\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n-\n-\t/* Only iterate over packs that are not marked as kept in-core. */\n-\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n-\n-\t/* Only iterate over packs that do not have .keep files. */\n-\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n-};\n-\n-/*\n- * Iterate over all accessible loose objects without respect to\n- * reachability. By default, this includes both local and alternate objects.\n- * The order in which objects are visited is unspecified.\n- *\n- * Any flags specific to packs are ignored.\n- */\n-int for_each_loose_object(each_loose_object_fn, void *,\n-\t\t\t  enum for_each_object_flags flags);\n-\n-/*\n- * Iterate over all accessible packed objects without respect to reachability.\n- * By default, this includes both local and alternate packs.\n- *\n- * Note that some objects may appear twice if they are found in multiple packs.\n- * Each pack is visited in an unspecified order. By default, objects within a\n- * pack are visited in pack-idx order (i.e., sorted by oid).\n- */\n-typedef int each_packed_object_fn(const struct object_id *oid,\n-\t\t\t\t  struct packed_git *pack,\n-\t\t\t\t  uint32_t pos,\n-\t\t\t\t  void *data);\n-int for_each_object_in_pack(struct packed_git *p,\n-\t\t\t    each_packed_object_fn, void *data,\n-\t\t\t    enum for_each_object_flags flags);\n-int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n-\t\t\t   void *data, enum for_each_object_flags flags);\n-\n-void *read_object_with_reference(struct repository *r,\n-\t\t\t\t const struct object_id *oid,\n-\t\t\t\t enum object_type required_type,\n-\t\t\t\t unsigned long *size,\n-\t\t\t\t struct object_id *oid_ret);\n-\n-#endif /* OBJECT_STORE_LL_H */\ndiff --git a/object-store.c b/object-store.c\nindex 0f1dcc113ed..6ab50d25d3e 100644\n--- a/object-store.c\n+++ b/object-store.c\n@@ -4,9 +4,11 @@\n #include \"abspath.h\"\n #include \"commit-graph.h\"\n #include \"config.h\"\n+#include \"dir.h\"\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n+#include \"khash.h\"\n #include \"lockfile.h\"\n #include \"loose.h\"\n #include \"object-file-convert.h\"\n@@ -24,6 +26,9 @@\n #include \"submodule.h\"\n #include \"write-or-die.h\"\n \n+KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n+\tstruct object_directory *, 1, fspathhash, fspatheq)\n+\n /*\n  * This is meant to hold a *small* number of objects that you would\n  * want repo_read_object_file() to be able to return, but yet you do not want\ndiff --git a/object-store.h b/object-store.h\nindex 1b3e3d7d014..46961dc9542 100644\n--- a/object-store.h\n+++ b/object-store.h\n@@ -1,11 +1,517 @@\n #ifndef OBJECT_STORE_H\n #define OBJECT_STORE_H\n \n-#include \"khash.h\"\n-#include \"dir.h\"\n-#include \"object-store-ll.h\"\n+#include \"hashmap.h\"\n+#include \"object.h\"\n+#include \"list.h\"\n+#include \"oidset.h\"\n+#include \"thread-utils.h\"\n \n-KHASH_INIT(odb_path_map, const char * /* key: odb_path */,\n-\tstruct object_directory *, 1, fspathhash, fspatheq)\n+struct oidmap;\n+struct oidtree;\n+struct strbuf;\n+struct repository;\n+\n+struct object_directory {\n+\tstruct object_directory *next;\n+\n+\t/*\n+\t * Used to store the results of readdir(3) calls when we are OK\n+\t * sacrificing accuracy due to races for speed. That includes\n+\t * object existence with OBJECT_INFO_QUICK, as well as\n+\t * our search for unique abbreviated hashes. Don't use it for tasks\n+\t * requiring greater accuracy!\n+\t *\n+\t * Be sure to call odb_load_loose_cache() before using.\n+\t */\n+\tuint32_t loose_objects_subdir_seen[8]; /* 256 bits */\n+\tstruct oidtree *loose_objects_cache;\n+\n+\t/* Map between object IDs for loose objects. */\n+\tstruct loose_object_map *loose_map;\n+\n+\t/*\n+\t * This is a temporary object store created by the tmp_objdir\n+\t * facility. Disable ref updates since the objects in the store\n+\t * might be discarded on rollback.\n+\t */\n+\tint disable_ref_updates;\n+\n+\t/*\n+\t * This object store is ephemeral, so there is no need to fsync.\n+\t */\n+\tint will_destroy;\n+\n+\t/*\n+\t * Path to the alternative object store. If this is a relative path,\n+\t * it is relative to the current working directory.\n+\t */\n+\tchar *path;\n+};\n+\n+void prepare_alt_odb(struct repository *r);\n+int has_alt_odb(struct repository *r);\n+char *compute_alternate_path(const char *path, struct strbuf *err);\n+struct object_directory *find_odb(struct repository *r, const char *obj_dir);\n+typedef int alt_odb_fn(struct object_directory *, void *);\n+int foreach_alt_odb(alt_odb_fn, void*);\n+typedef void alternate_ref_fn(const struct object_id *oid, void *);\n+void for_each_alternate_ref(alternate_ref_fn, void *);\n+\n+/*\n+ * Add the directory to the on-disk alternates file; the new entry will also\n+ * take effect in the current process.\n+ */\n+void add_to_alternates_file(const char *dir);\n+\n+/*\n+ * Add the directory to the in-memory list of alternates (along with any\n+ * recursive alternates it points to), but do not modify the on-disk alternates\n+ * file.\n+ */\n+void add_to_alternates_memory(const char *dir);\n+\n+/*\n+ * Replace the current writable object directory with the specified temporary\n+ * object directory; returns the former primary object directory.\n+ */\n+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);\n+\n+/*\n+ * Restore a previous ODB replaced by set_temporary_main_odb.\n+ */\n+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);\n+\n+/*\n+ * Populate and return the loose object cache array corresponding to the\n+ * given object ID.\n+ */\n+struct oidtree *odb_loose_cache(struct object_directory *odb,\n+\t\t\t\t  const struct object_id *oid);\n+\n+/* Empty the loose object cache for the specified object directory. */\n+void odb_clear_loose_cache(struct object_directory *odb);\n+\n+struct packed_git {\n+\tstruct hashmap_entry packmap_ent;\n+\tstruct packed_git *next;\n+\tstruct list_head mru;\n+\tstruct pack_window *windows;\n+\toff_t pack_size;\n+\tconst void *index_data;\n+\tsize_t index_size;\n+\tuint32_t num_objects;\n+\tsize_t crc_offset;\n+\tstruct oidset bad_objects;\n+\tint index_version;\n+\ttime_t mtime;\n+\tint pack_fd;\n+\tint index;              /* for builtin/pack-objects.c */\n+\tunsigned pack_local:1,\n+\t\t pack_keep:1,\n+\t\t pack_keep_in_core:1,\n+\t\t freshened:1,\n+\t\t do_not_close:1,\n+\t\t pack_promisor:1,\n+\t\t multi_pack_index:1,\n+\t\t is_cruft:1;\n+\tunsigned char hash[GIT_MAX_RAWSZ];\n+\tstruct revindex_entry *revindex;\n+\tconst uint32_t *revindex_data;\n+\tconst uint32_t *revindex_map;\n+\tsize_t revindex_size;\n+\t/*\n+\t * mtimes_map points at the beginning of the memory mapped region of\n+\t * this pack's corresponding .mtimes file, and mtimes_size is the size\n+\t * of that .mtimes file\n+\t */\n+\tconst uint32_t *mtimes_map;\n+\tsize_t mtimes_size;\n+\n+\t/* repo denotes the repository this packfile belongs to */\n+\tstruct repository *repo;\n+\n+\t/* something like \".git/objects/pack/xxxxx.pack\" */\n+\tchar pack_name[FLEX_ARRAY]; /* more */\n+};\n+\n+struct multi_pack_index;\n+\n+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,\n+\t\t\t\t     const struct hashmap_entry *entry,\n+\t\t\t\t     const struct hashmap_entry *entry2,\n+\t\t\t\t     const void *keydata)\n+{\n+\tconst char *key = keydata;\n+\tconst struct packed_git *pg1, *pg2;\n+\n+\tpg1 = container_of(entry, const struct packed_git, packmap_ent);\n+\tpg2 = container_of(entry2, const struct packed_git, packmap_ent);\n+\n+\treturn strcmp(pg1->pack_name, key ? key : pg2->pack_name);\n+}\n+\n+struct cached_object_entry;\n+\n+struct raw_object_store {\n+\t/*\n+\t * Set of all object directories; the main directory is first (and\n+\t * cannot be NULL after initialization). Subsequent directories are\n+\t * alternates.\n+\t */\n+\tstruct object_directory *odb;\n+\tstruct object_directory **odb_tail;\n+\tstruct kh_odb_path_map *odb_by_path;\n+\n+\tint loaded_alternates;\n+\n+\t/*\n+\t * A list of alternate object directories loaded from the environment;\n+\t * this should not generally need to be accessed directly, but will\n+\t * populate the \"odb\" list when prepare_alt_odb() is run.\n+\t */\n+\tchar *alternate_db;\n+\n+\t/*\n+\t * Objects that should be substituted by other objects\n+\t * (see git-replace(1)).\n+\t */\n+\tstruct oidmap *replace_map;\n+\tunsigned replace_map_initialized : 1;\n+\tpthread_mutex_t replace_mutex; /* protect object replace functions */\n+\n+\tstruct commit_graph *commit_graph;\n+\tunsigned commit_graph_attempted : 1; /* if loading has been attempted */\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c and midx.c\n+\t */\n+\tstruct multi_pack_index *multi_pack_index;\n+\n+\t/*\n+\t * private data\n+\t *\n+\t * should only be accessed directly by packfile.c\n+\t */\n+\n+\tstruct packed_git *packed_git;\n+\t/* A most-recently-used ordered version of the packed_git list. */\n+\tstruct list_head packed_git_mru;\n+\n+\tstruct {\n+\t\tstruct packed_git **packs;\n+\t\tunsigned flags;\n+\t} kept_pack_cache;\n+\n+\t/*\n+\t * This is meant to hold a *small* number of objects that you would\n+\t * want repo_read_object_file() to be able to return, but yet you do not want\n+\t * to write them into the object store (e.g. a browse-only\n+\t * application).\n+\t */\n+\tstruct cached_object_entry *cached_objects;\n+\tsize_t cached_object_nr, cached_object_alloc;\n+\n+\t/*\n+\t * A map of packfiles to packed_git structs for tracking which\n+\t * packs have been loaded already.\n+\t */\n+\tstruct hashmap pack_map;\n+\n+\t/*\n+\t * A fast, rough count of the number of objects in the repository.\n+\t * These two fields are not meant for direct access. Use\n+\t * repo_approximate_object_count() instead.\n+\t */\n+\tunsigned long approximate_object_count;\n+\tunsigned approximate_object_count_valid : 1;\n+\n+\t/*\n+\t * Whether packed_git has already been populated with this repository's\n+\t * packs.\n+\t */\n+\tunsigned packed_git_initialized : 1;\n+};\n+\n+struct raw_object_store *raw_object_store_new(void);\n+void raw_object_store_clear(struct raw_object_store *o);\n+\n+/*\n+ * Create a temporary file rooted in the object database directory, or\n+ * die on failure. The filename is taken from \"pattern\", which should have the\n+ * usual \"XXXXXX\" trailer, and the resulting filename is written into the\n+ * \"template\" buffer. Returns the open descriptor.\n+ */\n+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);\n+\n+/*\n+ * Create a pack .keep file named \"name\" (which should generally be the output\n+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on\n+ * error.\n+ */\n+int odb_pack_keep(const char *name);\n+\n+/*\n+ * Put in `buf` the name of the file in the local object database that\n+ * would be used to store a loose object with the specified oid.\n+ */\n+const char *loose_object_path(struct repository *r, struct strbuf *buf,\n+\t\t\t      const struct object_id *oid);\n+\n+void *map_loose_object(struct repository *r, const struct object_id *oid,\n+\t\t       unsigned long *size);\n+\n+void *repo_read_object_file(struct repository *r,\n+\t\t\t    const struct object_id *oid,\n+\t\t\t    enum object_type *type,\n+\t\t\t    unsigned long *size);\n+\n+/* Read and unpack an object file into memory, write memory to an object file */\n+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);\n+\n+void hash_object_file(const struct git_hash_algo *algo, const void *buf,\n+\t\t      unsigned long len, enum object_type type,\n+\t\t      struct object_id *oid);\n+\n+/*\n+ * Add an object file to the in-memory object store, without writing it\n+ * to disk.\n+ *\n+ * Callers are responsible for calling write_object_file to record the\n+ * object in persistent storage before writing any other new objects\n+ * that reference it.\n+ */\n+int pretend_object_file(struct repository *repo,\n+\t\t\tvoid *buf, unsigned long len, enum object_type type,\n+\t\t\tstruct object_id *oid);\n+\n+struct object_info {\n+\t/* Request */\n+\tenum object_type *typep;\n+\tunsigned long *sizep;\n+\toff_t *disk_sizep;\n+\tstruct object_id *delta_base_oid;\n+\tstruct strbuf *type_name;\n+\tvoid **contentp;\n+\n+\t/* Response */\n+\tenum {\n+\t\tOI_CACHED,\n+\t\tOI_LOOSE,\n+\t\tOI_PACKED,\n+\t\tOI_DBCACHED\n+\t} whence;\n+\tunion {\n+\t\t/*\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } cached;\n+\t\t * struct {\n+\t\t * \t... Nothing to expose in this case\n+\t\t * } loose;\n+\t\t */\n+\t\tstruct {\n+\t\t\tstruct packed_git *pack;\n+\t\t\toff_t offset;\n+\t\t\tunsigned int is_delta;\n+\t\t} packed;\n+\t} u;\n+};\n+\n+/*\n+ * Initializer for a \"struct object_info\" that wants no items. You may\n+ * also memset() the memory to all-zeroes.\n+ */\n+#define OBJECT_INFO_INIT { 0 }\n+\n+/* Invoke lookup_replace_object() on the given hash */\n+#define OBJECT_INFO_LOOKUP_REPLACE 1\n+/* Allow reading from a loose object file of unknown/bogus type */\n+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2\n+/* Do not retry packed storage after checking packed and loose storage */\n+#define OBJECT_INFO_QUICK 8\n+/*\n+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is\n+ * nonzero).\n+ */\n+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16\n+/*\n+ * This is meant for bulk prefetching of missing blobs in a partial\n+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK\n+ */\n+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)\n+\n+/* Die if object corruption (not just an object being missing) was detected. */\n+#define OBJECT_INFO_DIE_IF_CORRUPT 32\n+\n+int oid_object_info_extended(struct repository *r,\n+\t\t\t     const struct object_id *,\n+\t\t\t     struct object_info *, unsigned flags);\n+\n+/* Retry packed storage after checking packed and loose storage */\n+#define HAS_OBJECT_RECHECK_PACKED 1\n+\n+/*\n+ * Returns 1 if the object exists. This function will not lazily fetch objects\n+ * in a partial clone.\n+ */\n+int has_object(struct repository *r, const struct object_id *oid,\n+\t       unsigned flags);\n+\n+/*\n+ * These macros and functions are deprecated. If checking existence for an\n+ * object that is likely to be missing and/or whose absence is relatively\n+ * inconsequential (or is consequential but the caller is prepared to handle\n+ * it), use has_object(), which has better defaults (no lazy fetch in a partial\n+ * clone and no rechecking of packed storage). In the unlikely event that a\n+ * caller needs to assert existence of an object that it fully expects to\n+ * exist, and wants to trigger a lazy fetch in a partial clone, use\n+ * oid_object_info_extended() with a NULL struct object_info.\n+ *\n+ * These functions can be removed once all callers have migrated to\n+ * has_object() and/or oid_object_info_extended().\n+ */\n+int repo_has_object_file(struct repository *r, const struct object_id *oid);\n+int repo_has_object_file_with_flags(struct repository *r,\n+\t\t\t\t    const struct object_id *oid, int flags);\n+\n+void assert_oid_type(const struct object_id *oid, enum object_type expect);\n+\n+/*\n+ * Enabling the object read lock allows multiple threads to safely call the\n+ * following functions in parallel: repo_read_object_file(),\n+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().\n+ *\n+ * obj_read_lock() and obj_read_unlock() may also be used to protect other\n+ * section which cannot execute in parallel with object reading. Since the used\n+ * lock is a recursive mutex, these sections can even contain calls to object\n+ * reading functions. However, beware that in these cases zlib inflation won't\n+ * be performed in parallel, losing performance.\n+ *\n+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If\n+ * any of its callees end up calling it, this recursive call won't benefit from\n+ * parallel inflation.\n+ */\n+void enable_obj_read_lock(void);\n+void disable_obj_read_lock(void);\n+\n+extern int obj_read_use_lock;\n+extern pthread_mutex_t obj_read_mutex;\n+\n+static inline void obj_read_lock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_lock(&obj_read_mutex);\n+}\n+\n+static inline void obj_read_unlock(void)\n+{\n+\tif(obj_read_use_lock)\n+\t\tpthread_mutex_unlock(&obj_read_mutex);\n+}\n+\n+/*\n+ * Iterate over the files in the loose-object parts of the object\n+ * directory \"path\", triggering the following callbacks:\n+ *\n+ *  - loose_object is called for each loose object we find.\n+ *\n+ *  - loose_cruft is called for any files that do not appear to be\n+ *    loose objects. Note that we only look in the loose object\n+ *    directories \"objects/[0-9a-f]{2}/\", so we will not report\n+ *    \"objects/foobar\" as cruft.\n+ *\n+ *  - loose_subdir is called for each top-level hashed subdirectory\n+ *    of the object directory (e.g., \"$OBJDIR/f0\"). It is called\n+ *    after the objects in the directory are processed.\n+ *\n+ * Any callback that is NULL will be ignored. Callbacks returning non-zero\n+ * will end the iteration.\n+ *\n+ * In the \"buf\" variant, \"path\" is a strbuf which will also be used as a\n+ * scratch buffer, but restored to its original contents before\n+ * the function returns.\n+ */\n+typedef int each_loose_object_fn(const struct object_id *oid,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+typedef int each_loose_cruft_fn(const char *basename,\n+\t\t\t\tconst char *path,\n+\t\t\t\tvoid *data);\n+typedef int each_loose_subdir_fn(unsigned int nr,\n+\t\t\t\t const char *path,\n+\t\t\t\t void *data);\n+int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n+\t\t\t\tstruct strbuf *path,\n+\t\t\t\teach_loose_object_fn obj_cb,\n+\t\t\t\teach_loose_cruft_fn cruft_cb,\n+\t\t\t\teach_loose_subdir_fn subdir_cb,\n+\t\t\t\tvoid *data);\n+int for_each_loose_file_in_objdir(const char *path,\n+\t\t\t\t  each_loose_object_fn obj_cb,\n+\t\t\t\t  each_loose_cruft_fn cruft_cb,\n+\t\t\t\t  each_loose_subdir_fn subdir_cb,\n+\t\t\t\t  void *data);\n+int for_each_loose_file_in_objdir_buf(struct strbuf *path,\n+\t\t\t\t      each_loose_object_fn obj_cb,\n+\t\t\t\t      each_loose_cruft_fn cruft_cb,\n+\t\t\t\t      each_loose_subdir_fn subdir_cb,\n+\t\t\t\t      void *data);\n+\n+/* Flags for for_each_*_object() below. */\n+enum for_each_object_flags {\n+\t/* Iterate only over local objects, not alternates. */\n+\tFOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),\n+\n+\t/* Only iterate over packs obtained from the promisor remote. */\n+\tFOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),\n+\n+\t/*\n+\t * Visit objects within a pack in packfile order rather than .idx order\n+\t */\n+\tFOR_EACH_OBJECT_PACK_ORDER = (1<<2),\n+\n+\t/* Only iterate over packs that are not marked as kept in-core. */\n+\tFOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),\n+\n+\t/* Only iterate over packs that do not have .keep files. */\n+\tFOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),\n+};\n+\n+/*\n+ * Iterate over all accessible loose objects without respect to\n+ * reachability. By default, this includes both local and alternate objects.\n+ * The order in which objects are visited is unspecified.\n+ *\n+ * Any flags specific to packs are ignored.\n+ */\n+int for_each_loose_object(each_loose_object_fn, void *,\n+\t\t\t  enum for_each_object_flags flags);\n+\n+/*\n+ * Iterate over all accessible packed objects without respect to reachability.\n+ * By default, this includes both local and alternate packs.\n+ *\n+ * Note that some objects may appear twice if they are found in multiple packs.\n+ * Each pack is visited in an unspecified order. By default, objects within a\n+ * pack are visited in pack-idx order (i.e., sorted by oid).\n+ */\n+typedef int each_packed_object_fn(const struct object_id *oid,\n+\t\t\t\t  struct packed_git *pack,\n+\t\t\t\t  uint32_t pos,\n+\t\t\t\t  void *data);\n+int for_each_object_in_pack(struct packed_git *p,\n+\t\t\t    each_packed_object_fn, void *data,\n+\t\t\t    enum for_each_object_flags flags);\n+int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,\n+\t\t\t   void *data, enum for_each_object_flags flags);\n+\n+void *read_object_with_reference(struct repository *r,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type required_type,\n+\t\t\t\t unsigned long *size,\n+\t\t\t\t struct object_id *oid_ret);\n \n #endif /* OBJECT_STORE_H */\ndiff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c\nindex 3e190214d14..609a343ee3e 100644\n--- a/oss-fuzz/fuzz-pack-idx.c\n+++ b/oss-fuzz/fuzz-pack-idx.c\n@@ -1,5 +1,5 @@\n #include \"git-compat-util.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n \n int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 6a97b52b36d..4c7f7985e78 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -4,7 +4,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"diff.h\"\n #include \"revision.h\"\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 0dbd7c4ffe1..fb68d2ae637 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -17,7 +17,7 @@\n #include \"packfile.h\"\n #include \"repository.h\"\n #include \"trace2.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\n #include \"config.h\"\ndiff --git a/pack-check.c b/pack-check.c\nindex 95dcbbe9852..874897d6cba 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -8,7 +8,7 @@\n #include \"progress.h\"\n #include \"packfile.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct idx_entry {\n \toff_t                offset;\ndiff --git a/pack-mtimes.c b/pack-mtimes.c\nindex bcea28e521d..20900ca88d3 100644\n--- a/pack-mtimes.c\n+++ b/pack-mtimes.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-mtimes.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n \ndiff --git a/pack-objects.h b/pack-objects.h\nindex d73e3843c92..d1c4ae7f9b6 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -1,7 +1,7 @@\n #ifndef PACK_OBJECTS_H\n #define PACK_OBJECTS_H\n \n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"thread-utils.h\"\n #include \"pack.h\"\n \ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex 1ee7b49e206..37ad9c79268 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"pack-revindex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\ndiff --git a/packfile.c b/packfile.c\nindex 9d09f8bc726..d91016f1c7f 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -19,7 +19,7 @@\n #include \"tree-walk.h\"\n #include \"tree.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"midx.h\"\n #include \"commit-graph.h\"\n #include \"pack-revindex.h\"\ndiff --git a/path.c b/path.c\nindex 62d67166dff..4505bb78e8b 100644\n--- a/path.c\n+++ b/path.c\n@@ -15,7 +15,7 @@\n #include \"submodule-config.h\"\n #include \"path.h\"\n #include \"packfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"lockfile.h\"\n #include \"exec-cmd.h\"\n \ndiff --git a/promisor-remote.c b/promisor-remote.c\nindex 5801ebfd9b2..9d058586dfa 100644\n--- a/promisor-remote.c\n+++ b/promisor-remote.c\n@@ -3,7 +3,7 @@\n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"config.h\"\n #include \"trace2.h\"\ndiff --git a/protocol-caps.c b/protocol-caps.c\nindex 855f279c2f7..9b8db37a210 100644\n--- a/protocol-caps.c\n+++ b/protocol-caps.c\n@@ -6,7 +6,7 @@\n #include \"hash.h\"\n #include \"hex.h\"\n #include \"object.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n #include \"string-list.h\"\n #include \"strbuf.h\"\ndiff --git a/prune-packed.c b/prune-packed.c\nindex 7dad2fc0c16..c1d95a519d7 100644\n--- a/prune-packed.c\n+++ b/prune-packed.c\n@@ -2,7 +2,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"progress.h\"\n #include \"prune-packed.h\"\ndiff --git a/reachable.c b/reachable.c\nindex 1b26b9b1d76..16e23a38037 100644\n--- a/reachable.c\n+++ b/reachable.c\n@@ -14,7 +14,7 @@\n #include \"list-objects.h\"\n #include \"packfile.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"pack-mtimes.h\"\n #include \"config.h\"\ndiff --git a/read-cache.c b/read-cache.c\nindex 23028f43a11..570744bb566 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -20,7 +20,7 @@\n #include \"refs.h\"\n #include \"dir.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"tree.h\"\n #include \"commit.h\"\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 6da8d4c03b6..7a274633cfc 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"wildmatch.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"repo-settings.h\"\n #include \"repository.h\"\ndiff --git a/reflog.c b/reflog.c\nindex 1b5f031f6d7..60834a124d9 100644\n--- a/reflog.c\n+++ b/reflog.c\n@@ -3,7 +3,7 @@\n \n #include \"git-compat-util.h\"\n #include \"gettext.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"reflog.h\"\n #include \"refs.h\"\n #include \"revision.h\"\ndiff --git a/refs.c b/refs.c\nindex 1208f86629b..14f25c499cd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -19,7 +19,7 @@\n #include \"run-command.h\"\n #include \"hook.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"path.h\"\n #include \"submodule.h\"\ndiff --git a/remote.c b/remote.c\nindex e609cf5c56a..7fc657d98b8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -12,7 +12,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"commit.h\"\n #include \"diff.h\"\ndiff --git a/replace-object.c b/replace-object.c\nindex 9a3cdd809a9..7b8a09b5cb4 100644\n--- a/replace-object.c\n+++ b/replace-object.c\n@@ -2,7 +2,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"oidmap.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"refs.h\"\n #include \"repository.h\"\ndiff --git a/replace-object.h b/replace-object.h\nindex 66c41b938b4..ba478eb30c4 100644\n--- a/replace-object.h\n+++ b/replace-object.h\n@@ -3,7 +3,7 @@\n \n #include \"oidmap.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n \n struct replace_object {\n \tstruct oidmap_entry original;\ndiff --git a/repository.c b/repository.c\nindex 6cbaf2e3daa..9b3d6665fc6 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -1,7 +1,7 @@\n #include \"git-compat-util.h\"\n #include \"abspath.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\ndiff --git a/rerere.c b/rerere.c\nindex 0832cc54840..3cd37c5f0ae 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -18,7 +18,7 @@\n #include \"path.h\"\n #include \"pathspec.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strmap.h\"\n \n #define RESOLVED 0\ndiff --git a/revision.c b/revision.c\nindex b536c4a29ad..352e18b1f97 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -8,7 +8,7 @@\n #include \"hex.h\"\n #include \"object-name.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oidset.h\"\n #include \"tag.h\"\n #include \"blob.h\"\ndiff --git a/send-pack.c b/send-pack.c\nindex 856a65d5f5a..5005689cb55 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -4,7 +4,7 @@\n #include \"date.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"run-command.h\"\ndiff --git a/sequencer.c b/sequencer.c\nindex 9fda9be9266..552051e275c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -13,7 +13,7 @@\n #include \"dir.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"object.h\"\n #include \"pager.h\"\n #include \"commit.h\"\ndiff --git a/server-info.c b/server-info.c\nindex f0646ac92a9..d6cd20a39d7 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -11,7 +11,7 @@\n #include \"packfile.h\"\n #include \"path.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"server-info.h\"\n #include \"strbuf.h\"\n #include \"tempfile.h\"\ndiff --git a/shallow.c b/shallow.c\nindex 06c3266a3e0..2f82ebd6e3f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -5,7 +5,7 @@\n #include \"repository.h\"\n #include \"tempfile.h\"\n #include \"lockfile.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"pkt-line.h\"\ndiff --git a/streaming.c b/streaming.c\nindex 018b794d252..127d6b5d6ac 100644\n--- a/streaming.c\n+++ b/streaming.c\n@@ -10,7 +10,7 @@\n #include \"streaming.h\"\n #include \"repository.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"replace-object.h\"\n #include \"packfile.h\"\n \ndiff --git a/submodule-config.c b/submodule-config.c\nindex d82b404b73e..8630e27947d 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -13,7 +13,7 @@\n #include \"submodule.h\"\n #include \"strbuf.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"parse-options.h\"\n #include \"thread-utils.h\"\n #include \"tree-walk.h\"\ndiff --git a/submodule.c b/submodule.c\nindex 218c8c17603..ead3fb5dadc 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -27,7 +27,7 @@\n #include \"parse-options.h\"\n #include \"object-file.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit-reach.h\"\n #include \"read-cache-ll.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c\nindex f8f9afbb5b1..50f5941bff7 100644\n--- a/t/helper/test-pack-mtimes.c\n+++ b/t/helper/test-pack-mtimes.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"strbuf.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"packfile.h\"\n #include \"pack-mtimes.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c\nindex a1af9710c31..34f1aee5581 100644\n--- a/t/helper/test-partial-clone.c\n+++ b/t/helper/test-partial-clone.c\n@@ -1,7 +1,7 @@\n #include \"test-tool.h\"\n #include \"hex.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"setup.h\"\n \n /*\ndiff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c\nindex 811dde1cb3c..8b413b644be 100644\n--- a/t/helper/test-read-graph.c\n+++ b/t/helper/test-read-graph.c\n@@ -3,7 +3,7 @@\n #include \"test-tool.h\"\n #include \"commit-graph.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"bloom.h\"\n #include \"setup.h\"\n \ndiff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c\nindex fc632369618..ac81390899a 100644\n--- a/t/helper/test-read-midx.c\n+++ b/t/helper/test-read-midx.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"midx.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"pack-bitmap.h\"\n #include \"packfile.h\"\n #include \"setup.h\"\ndiff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c\nindex 2ff67c067ac..4cfc7c90b59 100644\n--- a/t/helper/test-ref-store.c\n+++ b/t/helper/test-ref-store.c\n@@ -5,7 +5,7 @@\n #include \"refs.h\"\n #include \"setup.h\"\n #include \"worktree.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"path.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\ndiff --git a/tag.c b/tag.c\nindex 8d9e9e29304..05be39067cf 100644\n--- a/tag.c\n+++ b/tag.c\n@@ -5,7 +5,7 @@\n #include \"environment.h\"\n #include \"tag.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"tree.h\"\n #include \"blob.h\"\ndiff --git a/tmp-objdir.c b/tmp-objdir.c\nindex 31d16a4c2c5..c38fbeb5e8a 100644\n--- a/tmp-objdir.c\n+++ b/tmp-objdir.c\n@@ -10,7 +10,7 @@\n #include \"strbuf.h\"\n #include \"strvec.h\"\n #include \"quote.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"repository.h\"\n \n struct tmp_objdir {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a0333979656..90655d52378 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -6,7 +6,7 @@\n #include \"gettext.h\"\n #include \"hex.h\"\n #include \"object-file.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"trace2.h\"\n #include \"tree.h\"\n #include \"pathspec.h\"\ndiff --git a/tree.c b/tree.c\nindex ad86ad1ba99..b85f56267fb 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -4,7 +4,7 @@\n #include \"hex.h\"\n #include \"tree.h\"\n #include \"object-name.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"alloc.h\"\n #include \"tree-walk.h\"\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex cf5b73c84be..471837f0329 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -26,7 +26,7 @@\n #include \"symlinks.h\"\n #include \"trace2.h\"\n #include \"fsmonitor.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"promisor-remote.h\"\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 02ce6336028..30e4630f3a1 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -10,7 +10,7 @@\n #include \"pkt-line.h\"\n #include \"sideband.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"oid-array.h\"\n #include \"object.h\"\n #include \"commit.h\"\ndiff --git a/walker.c b/walker.c\nindex 1cf3da02193..4fedc19f346 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -5,7 +5,7 @@\n #include \"hex.h\"\n #include \"walker.h\"\n #include \"repository.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"commit.h\"\n #include \"strbuf.h\"\n #include \"tree.h\"\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 77712811ff1..1edcd319e6e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -5,7 +5,7 @@\n #include \"gettext.h\"\n #include \"config.h\"\n #include \"hex.h\"\n-#include \"object-store-ll.h\"\n+#include \"object-store.h\"\n #include \"strbuf.h\"\n #include \"xdiff-interface.h\"\n #include \"xdiff/xtypes.h\"\n\n-- \n2.49.0.805.g082f7c87e0.dirty\n\n"},{"id":"516202","messageId":"xmqqbjsxo40k.fsf@gitster.g","threadId":"63270","inReplyTo":"Z_4klRXH1wwv7JXJ@pks.im","subject":"Re: [PATCH v2 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-15T15:09:31Z","receivedAt":"2025-04-15T15:09:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Fri, Apr 11, 2025 at 05:29:13PM -0400, Eric Sunshine wrote:\n>> On Fri, Apr 11, 2025 at 4:10 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> > Patrick Steinhardt <ps@pks.im> writes:\n>> > > The `safe_create_leading_directories()` function and its relatives are\n>> > > located in \"object-file.c\", which is not a good fit as they provide\n>> > > generic functionality not related to objects at all. Move them into\n>> > > \"dir.c\".\n>> >\n>> > It may be debatable that <dir.c>, which has traditionally been a\n>> > collection of read-only operations (mostly for exclude/ignore\n>> > processing), is a good place to host \"mkdir -p\", but it certainly is\n>> > better than having it in <object-file.c>\n>> \n>> I probably would have expected safe_create_leading_directories() to be\n>> moved to \"path.[hc]\" which already houses functions such as\n>> safe_create_dir(), normalize_path_copy(), ends_with_path_components(),\n>> longest_ancestor_length(), etc.\n>\n> Ah, good catch! Will adapt.\n\nYeah, path.* API sounds like a better fit, indeed.\n\nThanks, both.\n"},{"id":"516203","messageId":"xmqq7c3lo3xf.fsf@gitster.g","threadId":"63270","inReplyTo":"Z_4kmF8NjtERSsBz@pks.im","subject":"Re: [PATCH 1/9] object-file: move `safe_create_leading_directories()` into \"dir.c\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-15T15:11:24Z","receivedAt":"2025-04-15T15:11:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> better fix. I'm using that as an opportunity to rename the function to\n> `safe_create_dir_in_gitdir()` so that it matches `safe_create_dir()`,\n> which is functionally similar.\n\nExcellent.\n\nThanks.\n"},{"id":"516258","messageId":"CABPp-BFGeJ6eANb_5m4KSWRY_r9KCWarzya_qMkQNchKD5Q=xg@mail.gmail.com","threadId":"63270","inReplyTo":"20250415-pks-split-object-file-v3-0-6aa7db7ad7b0@pks.im","subject":"Re: [PATCH v3 00/10] Split up \"object-file.c\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-04-16T06:41:34Z","receivedAt":"2025-04-16T06:41:46Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Apr 15, 2025 at 2:38 AM Patrick Steinhardt <ps@pks.im> wrote:\n>\n> Hi,\n>\n> \"object-file.c\" is quite a grab-bag of all kinds of different functions.\n> Many of these functions aren't really a good fit though and should be\n> owned by a different subsystem. This patch series tries to split up\n> concerns a bit better by splitting out this functionality into other\n> files:\n>\n>   - `safe_create_leading_directories()` is moved into \"dir.c\".\n>   - `xmmap()` is moved into \"wrapper.c\".\n>   - `git_open_cloexec()` is moved into \"compat/open.c\".\n>   - Several functions attached to `struct index_state` are moved into\n>     \"read-cache.c\".\n>   - Several functions related to `struct object_store` are moved into a\n>     new file \"object-store.c\".\n>\n> \"object-file.c\" now mostly contains logic to read and write loose object\n> files, whereas \"object-store.c\" contains the higher-level logic to\n> manage different object directories for a repository. Eventually, these\n> will become the loose object backend as well as the `struct ref_store`\n> equivalent for objects, respectively.\n>\n> The series is built on top of 9d22ac51228 (The third batch, 2025-04-07)\n> with ps/object-wo-the-repository at 9442b1c919a (Merge remote-tracking\n> branch 'junio/ps/object-wo-the-repository' into HEAD, 2025-04-08) merged\n> into it.\n>\n> Changes in v2:\n>   - Fix a grammar issue in one of the commit messages.\n>   - Link to v1: https://lore.kernel.org/r/20250408-pks-split-object-file-v1-0-f1fd50191143@pks.im\n>\n> Changes in v3:\n>   - Rename `mkdir_in_gitdir()` to `safe_create_dir_in_gitdir()` to match\n>     naming of similar functions.\n>   - Move `safe_create_leading_directories()` et al into \"path.c\" instead\n>     of into \"dir.c\". This also requires us to start injecting a repo via\n>     parameters as \"path.c\" doesn't have `the_repository` available\n>     anymore.\n>   - Drop the commit that moves `index_blob_stream()` and related\n>     functions.\n>   - Expand the reasoning why we want to have cached objects per object\n>     store instead of globally.\n>   - Drop `index_blob_stream()`, which is a trivial wrapper around\n>     `index_blob_bulk_checkin()`.\n>   - Link to v2: https://lore.kernel.org/r/20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im\n\nv3 also addressed my feedback on v1 & v2.\n\n[...]\n> Range-diff versus v2:\n\nI read over the range-diff and the three new patches (1, 2, & 7); this\nround looks good to me.  I particularly like the extended rationale in\nthe commit message for what is now patch 9.\n"},{"id":"516260","messageId":"Z_9f7DqcAjiUUXgC@pks.im","threadId":"63270","inReplyTo":"CABPp-BFGeJ6eANb_5m4KSWRY_r9KCWarzya_qMkQNchKD5Q=xg@mail.gmail.com","subject":"Re: [PATCH v3 00/10] Split up \"object-file.c\"","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-04-16T07:44:44Z","receivedAt":"2025-04-16T08:19:22Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Apr 15, 2025 at 11:41:34PM -0700, Elijah Newren wrote:\n> On Tue, Apr 15, 2025 at 2:38 AM Patrick Steinhardt <ps@pks.im> wrote:\n> > Changes in v3:\n> >   - Rename `mkdir_in_gitdir()` to `safe_create_dir_in_gitdir()` to match\n> >     naming of similar functions.\n> >   - Move `safe_create_leading_directories()` et al into \"path.c\" instead\n> >     of into \"dir.c\". This also requires us to start injecting a repo via\n> >     parameters as \"path.c\" doesn't have `the_repository` available\n> >     anymore.\n> >   - Drop the commit that moves `index_blob_stream()` and related\n> >     functions.\n> >   - Expand the reasoning why we want to have cached objects per object\n> >     store instead of globally.\n> >   - Drop `index_blob_stream()`, which is a trivial wrapper around\n> >     `index_blob_bulk_checkin()`.\n> >   - Link to v2: https://lore.kernel.org/r/20250411-pks-split-object-file-v2-0-2bea0c9033ae@pks.im\n> \n> v3 also addressed my feedback on v1 & v2.\n> \n> [...]\n> > Range-diff versus v2:\n> \n> I read over the range-diff and the three new patches (1, 2, & 7); this\n> round looks good to me.  I particularly like the extended rationale in\n> the commit message for what is now patch 9.\n\nThanks for your review!\n\nPatrick\n"},{"id":"516301","messageId":"xmqq7c3khybt.fsf@gitster.g","threadId":"63270","inReplyTo":"CABPp-BFGeJ6eANb_5m4KSWRY_r9KCWarzya_qMkQNchKD5Q=xg@mail.gmail.com","subject":"Re: [PATCH v3 00/10] Split up \"object-file.c\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-16T16:21:10Z","receivedAt":"2025-04-16T16:21:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> I read over the range-diff and the three new patches (1, 2, & 7); this\n> round looks good to me.  I particularly like the extended rationale in\n> the commit message for what is now patch 9.\n\nThanks, both, this round looked good to me, too.\n\n"}]}