odb/source-packed: wire up `reprepare()` callback

Move the logic to prepare and reprepare the "packed" source into "odb/source-packed.c" and wire it up as the `reprepare()` callback. Note that "preparing" a source is not yet generic. Eventually, it would probably make sense to turn the existing `reprepare()` callback into a `prepare()` callback with an optional flag to force re-preparing. But this step will be handled in a separate patch series. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 17, 2026 at 08:39 UTC 9ea4ef8586d8fa74bf45c1dd8da2256099abebbd
7 files changed +172 -177
builtin/grep.c
+1 -1
@@ -1363,7 +1363,7 @@ int cmd_grep(int argc,
1363 odb_prepare_alternates(the_repository->objects);
1364 for (source = the_repository->objects->sources; source; source = source->next) {
1365 struct odb_source_files *files = odb_source_files_downcast(source);
1366 - packfile_store_prepare(files->packed);
1366 + odb_source_packed_prepare(files->packed);
1367 }
1368 }
1369
midx.c
+1 -1
@@ -102,7 +102,7 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
102 struct multi_pack_index *get_multi_pack_index(struct odb_source *source)
103 {
104 struct odb_source_files *files = odb_source_files_downcast(source);
105 - packfile_store_prepare(files->packed);
105 + odb_source_packed_prepare(files->packed);
106 return files->packed->midx;
107 }
108
odb/source-files.c
+1 -1
@@ -45,7 +45,7 @@ static void odb_source_files_reprepare(struct odb_source *source)
45 {
46 struct odb_source_files *files = odb_source_files_downcast(source);
47 odb_source_reprepare(&files->loose->base);
48 - packfile_store_reprepare(files->packed);
48 + odb_source_reprepare(&files->packed->base);
49 }
50
51 static int odb_source_files_read_object_info(struct odb_source *source,
odb/source-packed.c
+157
@@ -1,10 +1,166 @@
1 #include "git-compat-util.h"
2 #include "abspath.h"
3 #include "chdir-notify.h"
4 +#include "dir.h"
5 +#include "mergesort.h"
6 #include "midx.h"
7 #include "odb/source-packed.h"
8 #include "packfile.h"
9
10 +void (*report_garbage)(unsigned seen_bits, const char *path);
11 +
12 +static void report_helper(const struct string_list *list,
13 + int seen_bits, int first, int last)
14 +{
15 + if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
16 + return;
17 +
18 + for (; first < last; first++)
19 + report_garbage(seen_bits, list->items[first].string);
20 +}
21 +
22 +static void report_pack_garbage(struct string_list *list)
23 +{
24 + int baselen = -1, first = 0, seen_bits = 0;
25 +
26 + if (!report_garbage)
27 + return;
28 +
29 + string_list_sort(list);
30 +
31 + for (size_t i = 0; i < list->nr; i++) {
32 + const char *path = list->items[i].string;
33 + if (baselen != -1 &&
34 + strncmp(path, list->items[first].string, baselen)) {
35 + report_helper(list, seen_bits, first, i);
36 + baselen = -1;
37 + seen_bits = 0;
38 + }
39 + if (baselen == -1) {
40 + const char *dot = strrchr(path, '.');
41 + if (!dot) {
42 + report_garbage(PACKDIR_FILE_GARBAGE, path);
43 + continue;
44 + }
45 + baselen = dot - path + 1;
46 + first = i;
47 + }
48 + if (!strcmp(path + baselen, "pack"))
49 + seen_bits |= 1;
50 + else if (!strcmp(path + baselen, "idx"))
51 + seen_bits |= 2;
52 + }
53 + report_helper(list, seen_bits, first, list->nr);
54 +}
55 +
56 +struct prepare_pack_data {
57 + struct odb_source *source;
58 + struct string_list *garbage;
59 +};
60 +
61 +static void prepare_pack(const char *full_name, size_t full_name_len,
62 + const char *file_name, void *_data)
63 +{
64 + struct prepare_pack_data *data = (struct prepare_pack_data *)_data;
65 + struct odb_source_files *files = odb_source_files_downcast(data->source);
66 + size_t base_len = full_name_len;
67 +
68 + if (strip_suffix_mem(full_name, &base_len, ".idx") &&
69 + !(files->packed->midx &&
70 + midx_contains_pack(files->packed->midx, file_name))) {
71 + char *trimmed_path = xstrndup(full_name, full_name_len);
72 + packfile_store_load_pack(files->packed,
73 + trimmed_path, data->source->local);
74 + free(trimmed_path);
75 + }
76 +
77 + if (!report_garbage)
78 + return;
79 +
80 + if (!strcmp(file_name, "multi-pack-index") ||
81 + !strcmp(file_name, "multi-pack-index.d"))
82 + return;
83 + if (starts_with(file_name, "multi-pack-index") &&
84 + (ends_with(file_name, ".bitmap") || ends_with(file_name, ".rev")))
85 + return;
86 + if (ends_with(file_name, ".idx") ||
87 + ends_with(file_name, ".rev") ||
88 + ends_with(file_name, ".pack") ||
89 + ends_with(file_name, ".bitmap") ||
90 + ends_with(file_name, ".keep") ||
91 + ends_with(file_name, ".promisor") ||
92 + ends_with(file_name, ".mtimes"))
93 + string_list_append(data->garbage, full_name);
94 + else
95 + report_garbage(PACKDIR_FILE_GARBAGE, full_name);
96 +}
97 +
98 +static void prepare_packed_git_one(struct odb_source *source)
99 +{
100 + struct string_list garbage = STRING_LIST_INIT_DUP;
101 + struct prepare_pack_data data = {
102 + .source = source,
103 + .garbage = &garbage,
104 + };
105 +
106 + for_each_file_in_pack_dir(source->path, prepare_pack, &data);
107 +
108 + report_pack_garbage(data.garbage);
109 + string_list_clear(data.garbage, 0);
110 +}
111 +
112 +DEFINE_LIST_SORT(static, sort_packs, struct packfile_list_entry, next);
113 +
114 +static int sort_pack(const struct packfile_list_entry *a,
115 + const struct packfile_list_entry *b)
116 +{
117 + int st;
118 +
119 + /*
120 + * Local packs tend to contain objects specific to our
121 + * variant of the project than remote ones. In addition,
122 + * remote ones could be on a network mounted filesystem.
123 + * Favor local ones for these reasons.
124 + */
125 + st = a->pack->pack_local - b->pack->pack_local;
126 + if (st)
127 + return -st;
128 +
129 + /*
130 + * Younger packs tend to contain more recent objects,
131 + * and more recent objects tend to get accessed more
132 + * often.
133 + */
134 + if (a->pack->mtime < b->pack->mtime)
135 + return 1;
136 + else if (a->pack->mtime == b->pack->mtime)
137 + return 0;
138 + return -1;
139 +}
140 +
141 +void odb_source_packed_prepare(struct odb_source_packed *source)
142 +{
143 + if (source->initialized)
144 + return;
145 +
146 + prepare_multi_pack_index_one(&source->files->base);
147 + prepare_packed_git_one(&source->files->base);
148 +
149 + sort_packs(&source->packs.head, sort_pack);
150 + for (struct packfile_list_entry *e = source->packs.head; e; e = e->next)
151 + if (!e->next)
152 + source->packs.tail = e;
153 +
154 + source->initialized = true;
155 +}
156 +
157 +static void odb_source_packed_reprepare(struct odb_source *source)
158 +{
159 + struct odb_source_packed *packed = odb_source_packed_downcast(source);
160 + packed->initialized = false;
161 + odb_source_packed_prepare(packed);
162 +}
163 +
164 static void odb_source_packed_reparent(const char *name UNUSED,
165 const char *old_cwd,
166 const char *new_cwd,
@@ -58,6 +214,7 @@ struct odb_source_packed *odb_source_packed_new(struct odb_source_files *parent)
214
215 packed->base.free = odb_source_packed_free;
216 packed->base.close = odb_source_packed_close;
217 + packed->base.reprepare = odb_source_packed_reprepare;
218
219 if (!is_absolute_path(parent->base.path))
220 chdir_notify_register(NULL, odb_source_packed_reparent, packed);
odb/source-packed.h
+9
@@ -81,4 +81,13 @@ static inline struct odb_source_packed *odb_source_packed_downcast(struct odb_so
81 return container_of(source, struct odb_source_packed, base);
82 }
83
84 +/*
85 + * Prepare the source by loading packfiles and multi-pack indices for
86 + * all alternates. This becomes a no-op if the source is already prepared.
87 + *
88 + * It shouldn't typically be necessary to call this function directly, as
89 + * functions that access the source know to prepare it.
90 + */
91 +void odb_source_packed_prepare(struct odb_source_packed *source);
92 +
93 #endif
packfile.c
+3 -157
@@ -8,7 +8,6 @@
8 #include "pack.h"
9 #include "repository.h"
10 #include "dir.h"
11 -#include "mergesort.h"
11 #include "packfile.h"
12 #include "delta.h"
13 #include "hash-lookup.h"
@@ -812,52 +811,6 @@ struct packed_git *packfile_store_load_pack(struct odb_source_packed *store,
811 return p;
812 }
813
815 -void (*report_garbage)(unsigned seen_bits, const char *path);
816 -
817 -static void report_helper(const struct string_list *list,
818 - int seen_bits, int first, int last)
819 -{
820 - if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
821 - return;
822 -
823 - for (; first < last; first++)
824 - report_garbage(seen_bits, list->items[first].string);
825 -}
826 -
827 -static void report_pack_garbage(struct string_list *list)
828 -{
829 - int i, baselen = -1, first = 0, seen_bits = 0;
830 -
831 - if (!report_garbage)
832 - return;
833 -
834 - string_list_sort(list);
835 -
836 - for (i = 0; i < list->nr; i++) {
837 - const char *path = list->items[i].string;
838 - if (baselen != -1 &&
839 - strncmp(path, list->items[first].string, baselen)) {
840 - report_helper(list, seen_bits, first, i);
841 - baselen = -1;
842 - seen_bits = 0;
843 - }
844 - if (baselen == -1) {
845 - const char *dot = strrchr(path, '.');
846 - if (!dot) {
847 - report_garbage(PACKDIR_FILE_GARBAGE, path);
848 - continue;
849 - }
850 - baselen = dot - path + 1;
851 - first = i;
852 - }
853 - if (!strcmp(path + baselen, "pack"))
854 - seen_bits |= 1;
855 - else if (!strcmp(path + baselen, "idx"))
856 - seen_bits |= 2;
857 - }
858 - report_helper(list, seen_bits, first, list->nr);
859 -}
860 -
814 void for_each_file_in_pack_subdir(const char *objdir,
815 const char *subdir,
816 each_file_in_pack_dir_fn fn,
@@ -900,116 +853,9 @@ void for_each_file_in_pack_dir(const char *objdir,
853 for_each_file_in_pack_subdir(objdir, NULL, fn, data);
854 }
855
903 -struct prepare_pack_data {
904 - struct odb_source *source;
905 - struct string_list *garbage;
906 -};
907 -
908 -static void prepare_pack(const char *full_name, size_t full_name_len,
909 - const char *file_name, void *_data)
910 -{
911 - struct prepare_pack_data *data = (struct prepare_pack_data *)_data;
912 - struct odb_source_files *files = odb_source_files_downcast(data->source);
913 - size_t base_len = full_name_len;
914 -
915 - if (strip_suffix_mem(full_name, &base_len, ".idx") &&
916 - !(files->packed->midx &&
917 - midx_contains_pack(files->packed->midx, file_name))) {
918 - char *trimmed_path = xstrndup(full_name, full_name_len);
919 - packfile_store_load_pack(files->packed,
920 - trimmed_path, data->source->local);
921 - free(trimmed_path);
922 - }
923 -
924 - if (!report_garbage)
925 - return;
926 -
927 - if (!strcmp(file_name, "multi-pack-index") ||
928 - !strcmp(file_name, "multi-pack-index.d"))
929 - return;
930 - if (starts_with(file_name, "multi-pack-index") &&
931 - (ends_with(file_name, ".bitmap") || ends_with(file_name, ".rev")))
932 - return;
933 - if (ends_with(file_name, ".idx") ||
934 - ends_with(file_name, ".rev") ||
935 - ends_with(file_name, ".pack") ||
936 - ends_with(file_name, ".bitmap") ||
937 - ends_with(file_name, ".keep") ||
938 - ends_with(file_name, ".promisor") ||
939 - ends_with(file_name, ".mtimes"))
940 - string_list_append(data->garbage, full_name);
941 - else
942 - report_garbage(PACKDIR_FILE_GARBAGE, full_name);
943 -}
944 -
945 -static void prepare_packed_git_one(struct odb_source *source)
946 -{
947 - struct string_list garbage = STRING_LIST_INIT_DUP;
948 - struct prepare_pack_data data = {
949 - .source = source,
950 - .garbage = &garbage,
951 - };
952 -
953 - for_each_file_in_pack_dir(source->path, prepare_pack, &data);
954 -
955 - report_pack_garbage(data.garbage);
956 - string_list_clear(data.garbage, 0);
957 -}
958 -
959 -DEFINE_LIST_SORT(static, sort_packs, struct packfile_list_entry, next);
960 -
961 -static int sort_pack(const struct packfile_list_entry *a,
962 - const struct packfile_list_entry *b)
963 -{
964 - int st;
965 -
966 - /*
967 - * Local packs tend to contain objects specific to our
968 - * variant of the project than remote ones. In addition,
969 - * remote ones could be on a network mounted filesystem.
970 - * Favor local ones for these reasons.
971 - */
972 - st = a->pack->pack_local - b->pack->pack_local;
973 - if (st)
974 - return -st;
975 -
976 - /*
977 - * Younger packs tend to contain more recent objects,
978 - * and more recent objects tend to get accessed more
979 - * often.
980 - */
981 - if (a->pack->mtime < b->pack->mtime)
982 - return 1;
983 - else if (a->pack->mtime == b->pack->mtime)
984 - return 0;
985 - return -1;
986 -}
987 -
988 -void packfile_store_prepare(struct odb_source_packed *store)
989 -{
990 - if (store->initialized)
991 - return;
992 -
993 - prepare_multi_pack_index_one(&store->files->base);
994 - prepare_packed_git_one(&store->files->base);
995 -
996 - sort_packs(&store->packs.head, sort_pack);
997 - for (struct packfile_list_entry *e = store->packs.head; e; e = e->next)
998 - if (!e->next)
999 - store->packs.tail = e;
1000 -
1001 - store->initialized = true;
1002 -}
1003 -
1004 -void packfile_store_reprepare(struct odb_source_packed *store)
1005 -{
1006 - store->initialized = false;
1007 - packfile_store_prepare(store);
1008 -}
1009 -
856 struct packfile_list_entry *packfile_store_get_packs(struct odb_source_packed *store)
857 {
1012 - packfile_store_prepare(store);
858 + odb_source_packed_prepare(store);
859
860 if (store->midx) {
861 struct multi_pack_index *m = store->midx;
@@ -2083,7 +1929,7 @@ static int find_pack_entry(struct odb_source_packed *store,
1929 {
1930 struct packfile_list_entry *l;
1931
2086 - packfile_store_prepare(store);
1932 + odb_source_packed_prepare(store);
1933 if (store->midx && fill_midx_entry(store->midx, oid, e))
1934 return 1;
1935
@@ -2130,7 +1976,7 @@ int packfile_store_read_object_info(struct odb_source_packed *store,
1976 * been added since the last time we have prepared the packfile store.
1977 */
1978 if (flags & OBJECT_INFO_SECOND_READ)
2133 - packfile_store_reprepare(store);
1979 + odb_source_reprepare(&store->base);
1980
1981 if (!find_pack_entry(store, oid, &e))
1982 return 1;
packfile.h
-17
@@ -55,23 +55,6 @@ struct packed_git {
55 char pack_name[FLEX_ARRAY]; /* more */
56 };
57
58 -/*
59 - * Prepare the packfile store by loading packfiles and multi-pack indices for
60 - * all alternates. This becomes a no-op if the store is already prepared.
61 - *
62 - * It shouldn't typically be necessary to call this function directly, as
63 - * functions that access the store know to prepare it.
64 - */
65 -void packfile_store_prepare(struct odb_source_packed *store);
66 -
67 -/*
68 - * Clear the packfile caches and try to look up any new packfiles that have
69 - * appeared since last preparing the packfiles store.
70 - *
71 - * This function must be called under the `odb_read_lock()`.
72 - */
73 -void packfile_store_reprepare(struct odb_source_packed *store);
74 -
58 /*
59 * Add the pack to the store so that contained objects become accessible via
60 * the store. This moves ownership into the store.