repack: remove pack_geometry API from the builtin
Now that the pack_geometry API is fully factored and isolated from the rest of the builtin, declare it within repack.h and move its implementation to "repack-geometry.c" as a separate component. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Taylor Blau committed
Oct 15, 2025 at 18:28 UTC
62d3fa09b3890631af7c572cb6132088a14d2653
5 files changed
+256
-235
Makefile
+1
@@ -1137,6 +1137,7 @@ LIB_OBJS += refs/ref-cache.o
1137
LIB_OBJS += refspec.o
1138
LIB_OBJS += remote.o
1139
LIB_OBJS += repack.o
1140
+LIB_OBJS += repack-geometry.o
1141
LIB_OBJS += repack-promisor.o
1142
LIB_OBJS += replace-object.o
1143
LIB_OBJS += repo-settings.o
builtin/repack.c
-235
@@ -107,241 +107,6 @@ static int repack_config(const char *var, const char *value,
107
return git_default_config(var, value, ctx, cb);
108
}
109
110
-struct pack_geometry {
111
- struct packed_git **pack;
112
- uint32_t pack_nr, pack_alloc;
113
- uint32_t split;
114
-
115
- int split_factor;
116
-};
117
-
118
-static uint32_t pack_geometry_weight(struct packed_git *p)
119
-{
120
- if (open_pack_index(p))
121
- die(_("cannot open index for %s"), p->pack_name);
122
- return p->num_objects;
123
-}
124
-
125
-static int pack_geometry_cmp(const void *va, const void *vb)
126
-{
127
- uint32_t aw = pack_geometry_weight(*(struct packed_git **)va),
128
- bw = pack_geometry_weight(*(struct packed_git **)vb);
129
-
130
- if (aw < bw)
131
- return -1;
132
- if (aw > bw)
133
- return 1;
134
- return 0;
135
-}
136
-
137
-static void pack_geometry_init(struct pack_geometry *geometry,
138
- struct existing_packs *existing,
139
- const struct pack_objects_args *args,
140
- int pack_kept_objects)
141
-{
142
- struct packfile_store *packs = existing->repo->objects->packfiles;
143
- struct packed_git *p;
144
- struct strbuf buf = STRBUF_INIT;
145
-
146
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
147
- if (args->local && !p->pack_local)
148
- /*
149
- * When asked to only repack local packfiles we skip
150
- * over any packfiles that are borrowed from alternate
151
- * object directories.
152
- */
153
- continue;
154
-
155
- if (!pack_kept_objects) {
156
- /*
157
- * Any pack that has its pack_keep bit set will
158
- * appear in existing->kept_packs below, but
159
- * this saves us from doing a more expensive
160
- * check.
161
- */
162
- if (p->pack_keep)
163
- continue;
164
-
165
- /*
166
- * The pack may be kept via the --keep-pack
167
- * option; check 'existing->kept_packs' to
168
- * determine whether to ignore it.
169
- */
170
- strbuf_reset(&buf);
171
- strbuf_addstr(&buf, pack_basename(p));
172
- strbuf_strip_suffix(&buf, ".pack");
173
-
174
- if (string_list_has_string(&existing->kept_packs, buf.buf))
175
- continue;
176
- }
177
- if (p->is_cruft)
178
- continue;
179
-
180
- ALLOC_GROW(geometry->pack,
181
- geometry->pack_nr + 1,
182
- geometry->pack_alloc);
183
-
184
- geometry->pack[geometry->pack_nr] = p;
185
- geometry->pack_nr++;
186
- }
187
-
188
- QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp);
189
- strbuf_release(&buf);
190
-}
191
-
192
-static void pack_geometry_split(struct pack_geometry *geometry)
193
-{
194
- uint32_t i;
195
- uint32_t split;
196
- off_t total_size = 0;
197
-
198
- if (!geometry->pack_nr) {
199
- geometry->split = geometry->pack_nr;
200
- return;
201
- }
202
-
203
- /*
204
- * First, count the number of packs (in descending order of size) which
205
- * already form a geometric progression.
206
- */
207
- for (i = geometry->pack_nr - 1; i > 0; i--) {
208
- struct packed_git *ours = geometry->pack[i];
209
- struct packed_git *prev = geometry->pack[i - 1];
210
-
211
- if (unsigned_mult_overflows(geometry->split_factor,
212
- pack_geometry_weight(prev)))
213
- die(_("pack %s too large to consider in geometric "
214
- "progression"),
215
- prev->pack_name);
216
-
217
- if (pack_geometry_weight(ours) <
218
- geometry->split_factor * pack_geometry_weight(prev))
219
- break;
220
- }
221
-
222
- split = i;
223
-
224
- if (split) {
225
- /*
226
- * Move the split one to the right, since the top element in the
227
- * last-compared pair can't be in the progression. Only do this
228
- * when we split in the middle of the array (otherwise if we got
229
- * to the end, then the split is in the right place).
230
- */
231
- split++;
232
- }
233
-
234
- /*
235
- * Then, anything to the left of 'split' must be in a new pack. But,
236
- * creating that new pack may cause packs in the heavy half to no longer
237
- * form a geometric progression.
238
- *
239
- * Compute an expected size of the new pack, and then determine how many
240
- * packs in the heavy half need to be joined into it (if any) to restore
241
- * the geometric progression.
242
- */
243
- for (i = 0; i < split; i++) {
244
- struct packed_git *p = geometry->pack[i];
245
-
246
- if (unsigned_add_overflows(total_size, pack_geometry_weight(p)))
247
- die(_("pack %s too large to roll up"), p->pack_name);
248
- total_size += pack_geometry_weight(p);
249
- }
250
- for (i = split; i < geometry->pack_nr; i++) {
251
- struct packed_git *ours = geometry->pack[i];
252
-
253
- if (unsigned_mult_overflows(geometry->split_factor,
254
- total_size))
255
- die(_("pack %s too large to roll up"), ours->pack_name);
256
-
257
- if (pack_geometry_weight(ours) <
258
- geometry->split_factor * total_size) {
259
- if (unsigned_add_overflows(total_size,
260
- pack_geometry_weight(ours)))
261
- die(_("pack %s too large to roll up"),
262
- ours->pack_name);
263
-
264
- split++;
265
- total_size += pack_geometry_weight(ours);
266
- } else
267
- break;
268
- }
269
-
270
- geometry->split = split;
271
-}
272
-
273
-static struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
274
-{
275
- uint32_t i;
276
-
277
- if (!geometry) {
278
- /*
279
- * No geometry means either an all-into-one repack (in which
280
- * case there is only one pack left and it is the largest) or an
281
- * incremental one.
282
- *
283
- * If repacking incrementally, then we could check the size of
284
- * all packs to determine which should be preferred, but leave
285
- * this for later.
286
- */
287
- return NULL;
288
- }
289
- if (geometry->split == geometry->pack_nr)
290
- return NULL;
291
-
292
- /*
293
- * The preferred pack is the largest pack above the split line. In
294
- * other words, it is the largest pack that does not get rolled up in
295
- * the geometric repack.
296
- */
297
- for (i = geometry->pack_nr; i > geometry->split; i--)
298
- /*
299
- * A pack that is not local would never be included in a
300
- * multi-pack index. We thus skip over any non-local packs.
301
- */
302
- if (geometry->pack[i - 1]->pack_local)
303
- return geometry->pack[i - 1];
304
-
305
- return NULL;
306
-}
307
-
308
-static void pack_geometry_remove_redundant(struct pack_geometry *geometry,
309
- struct string_list *names,
310
- struct existing_packs *existing,
311
- const char *packdir)
312
-{
313
- const struct git_hash_algo *algop = existing->repo->hash_algo;
314
- struct strbuf buf = STRBUF_INIT;
315
- uint32_t i;
316
-
317
- for (i = 0; i < geometry->split; i++) {
318
- struct packed_git *p = geometry->pack[i];
319
- if (string_list_has_string(names, hash_to_hex_algop(p->hash,
320
- algop)))
321
- continue;
322
-
323
- strbuf_reset(&buf);
324
- strbuf_addstr(&buf, pack_basename(p));
325
- strbuf_strip_suffix(&buf, ".pack");
326
-
327
- if ((p->pack_keep) ||
328
- (string_list_has_string(&existing->kept_packs, buf.buf)))
329
- continue;
330
-
331
- repack_remove_redundant_pack(existing->repo, packdir, buf.buf);
332
- }
333
-
334
- strbuf_release(&buf);
335
-}
336
-
337
-static void pack_geometry_release(struct pack_geometry *geometry)
338
-{
339
- if (!geometry)
340
- return;
341
-
342
- free(geometry->pack);
343
-}
344
-
110
static int midx_has_unknown_packs(char **midx_pack_names,
111
size_t midx_pack_names_nr,
112
struct string_list *include,
meson.build
+1
@@ -463,6 +463,7 @@ libgit_sources = [
463
'reftable/writer.c',
464
'remote.c',
465
'repack.c',
466
+ 'repack-geometry.c',
467
'repack-promisor.c',
468
'replace-object.c',
469
'repo-settings.c',
repack-geometry.c
new
+234
@@ -0,0 +1,234 @@
1
+#define DISABLE_SIGN_COMPARE_WARNINGS
2
+
3
+#include "git-compat-util.h"
4
+#include "repack.h"
5
+#include "repository.h"
6
+#include "hex.h"
7
+#include "packfile.h"
8
+
9
+static uint32_t pack_geometry_weight(struct packed_git *p)
10
+{
11
+ if (open_pack_index(p))
12
+ die(_("cannot open index for %s"), p->pack_name);
13
+ return p->num_objects;
14
+}
15
+
16
+static int pack_geometry_cmp(const void *va, const void *vb)
17
+{
18
+ uint32_t aw = pack_geometry_weight(*(struct packed_git **)va),
19
+ bw = pack_geometry_weight(*(struct packed_git **)vb);
20
+
21
+ if (aw < bw)
22
+ return -1;
23
+ if (aw > bw)
24
+ return 1;
25
+ return 0;
26
+}
27
+
28
+void pack_geometry_init(struct pack_geometry *geometry,
29
+ struct existing_packs *existing,
30
+ const struct pack_objects_args *args,
31
+ int pack_kept_objects)
32
+{
33
+ struct packfile_store *packs = existing->repo->objects->packfiles;
34
+ struct packed_git *p;
35
+ struct strbuf buf = STRBUF_INIT;
36
+
37
+ for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
38
+ if (args->local && !p->pack_local)
39
+ /*
40
+ * When asked to only repack local packfiles we skip
41
+ * over any packfiles that are borrowed from alternate
42
+ * object directories.
43
+ */
44
+ continue;
45
+
46
+ if (!pack_kept_objects) {
47
+ /*
48
+ * Any pack that has its pack_keep bit set will
49
+ * appear in existing->kept_packs below, but
50
+ * this saves us from doing a more expensive
51
+ * check.
52
+ */
53
+ if (p->pack_keep)
54
+ continue;
55
+
56
+ /*
57
+ * The pack may be kept via the --keep-pack
58
+ * option; check 'existing->kept_packs' to
59
+ * determine whether to ignore it.
60
+ */
61
+ strbuf_reset(&buf);
62
+ strbuf_addstr(&buf, pack_basename(p));
63
+ strbuf_strip_suffix(&buf, ".pack");
64
+
65
+ if (string_list_has_string(&existing->kept_packs, buf.buf))
66
+ continue;
67
+ }
68
+ if (p->is_cruft)
69
+ continue;
70
+
71
+ ALLOC_GROW(geometry->pack,
72
+ geometry->pack_nr + 1,
73
+ geometry->pack_alloc);
74
+
75
+ geometry->pack[geometry->pack_nr] = p;
76
+ geometry->pack_nr++;
77
+ }
78
+
79
+ QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp);
80
+ strbuf_release(&buf);
81
+}
82
+
83
+void pack_geometry_split(struct pack_geometry *geometry)
84
+{
85
+ uint32_t i;
86
+ uint32_t split;
87
+ off_t total_size = 0;
88
+
89
+ if (!geometry->pack_nr) {
90
+ geometry->split = geometry->pack_nr;
91
+ return;
92
+ }
93
+
94
+ /*
95
+ * First, count the number of packs (in descending order of size) which
96
+ * already form a geometric progression.
97
+ */
98
+ for (i = geometry->pack_nr - 1; i > 0; i--) {
99
+ struct packed_git *ours = geometry->pack[i];
100
+ struct packed_git *prev = geometry->pack[i - 1];
101
+
102
+ if (unsigned_mult_overflows(geometry->split_factor,
103
+ pack_geometry_weight(prev)))
104
+ die(_("pack %s too large to consider in geometric "
105
+ "progression"),
106
+ prev->pack_name);
107
+
108
+ if (pack_geometry_weight(ours) <
109
+ geometry->split_factor * pack_geometry_weight(prev))
110
+ break;
111
+ }
112
+
113
+ split = i;
114
+
115
+ if (split) {
116
+ /*
117
+ * Move the split one to the right, since the top element in the
118
+ * last-compared pair can't be in the progression. Only do this
119
+ * when we split in the middle of the array (otherwise if we got
120
+ * to the end, then the split is in the right place).
121
+ */
122
+ split++;
123
+ }
124
+
125
+ /*
126
+ * Then, anything to the left of 'split' must be in a new pack. But,
127
+ * creating that new pack may cause packs in the heavy half to no longer
128
+ * form a geometric progression.
129
+ *
130
+ * Compute an expected size of the new pack, and then determine how many
131
+ * packs in the heavy half need to be joined into it (if any) to restore
132
+ * the geometric progression.
133
+ */
134
+ for (i = 0; i < split; i++) {
135
+ struct packed_git *p = geometry->pack[i];
136
+
137
+ if (unsigned_add_overflows(total_size, pack_geometry_weight(p)))
138
+ die(_("pack %s too large to roll up"), p->pack_name);
139
+ total_size += pack_geometry_weight(p);
140
+ }
141
+ for (i = split; i < geometry->pack_nr; i++) {
142
+ struct packed_git *ours = geometry->pack[i];
143
+
144
+ if (unsigned_mult_overflows(geometry->split_factor,
145
+ total_size))
146
+ die(_("pack %s too large to roll up"), ours->pack_name);
147
+
148
+ if (pack_geometry_weight(ours) <
149
+ geometry->split_factor * total_size) {
150
+ if (unsigned_add_overflows(total_size,
151
+ pack_geometry_weight(ours)))
152
+ die(_("pack %s too large to roll up"),
153
+ ours->pack_name);
154
+
155
+ split++;
156
+ total_size += pack_geometry_weight(ours);
157
+ } else
158
+ break;
159
+ }
160
+
161
+ geometry->split = split;
162
+}
163
+
164
+struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
165
+{
166
+ uint32_t i;
167
+
168
+ if (!geometry) {
169
+ /*
170
+ * No geometry means either an all-into-one repack (in which
171
+ * case there is only one pack left and it is the largest) or an
172
+ * incremental one.
173
+ *
174
+ * If repacking incrementally, then we could check the size of
175
+ * all packs to determine which should be preferred, but leave
176
+ * this for later.
177
+ */
178
+ return NULL;
179
+ }
180
+ if (geometry->split == geometry->pack_nr)
181
+ return NULL;
182
+
183
+ /*
184
+ * The preferred pack is the largest pack above the split line. In
185
+ * other words, it is the largest pack that does not get rolled up in
186
+ * the geometric repack.
187
+ */
188
+ for (i = geometry->pack_nr; i > geometry->split; i--)
189
+ /*
190
+ * A pack that is not local would never be included in a
191
+ * multi-pack index. We thus skip over any non-local packs.
192
+ */
193
+ if (geometry->pack[i - 1]->pack_local)
194
+ return geometry->pack[i - 1];
195
+
196
+ return NULL;
197
+}
198
+
199
+void pack_geometry_remove_redundant(struct pack_geometry *geometry,
200
+ struct string_list *names,
201
+ struct existing_packs *existing,
202
+ const char *packdir)
203
+{
204
+ const struct git_hash_algo *algop = existing->repo->hash_algo;
205
+ struct strbuf buf = STRBUF_INIT;
206
+ uint32_t i;
207
+
208
+ for (i = 0; i < geometry->split; i++) {
209
+ struct packed_git *p = geometry->pack[i];
210
+ if (string_list_has_string(names, hash_to_hex_algop(p->hash,
211
+ algop)))
212
+ continue;
213
+
214
+ strbuf_reset(&buf);
215
+ strbuf_addstr(&buf, pack_basename(p));
216
+ strbuf_strip_suffix(&buf, ".pack");
217
+
218
+ if ((p->pack_keep) ||
219
+ (string_list_has_string(&existing->kept_packs, buf.buf)))
220
+ continue;
221
+
222
+ repack_remove_redundant_pack(existing->repo, packdir, buf.buf);
223
+ }
224
+
225
+ strbuf_release(&buf);
226
+}
227
+
228
+void pack_geometry_release(struct pack_geometry *geometry)
229
+{
230
+ if (!geometry)
231
+ return;
232
+
233
+ free(geometry->pack);
234
+}
repack.h
+20
@@ -78,4 +78,24 @@ void repack_promisor_objects(struct repository *repo,
78
const struct pack_objects_args *args,
79
struct string_list *names, const char *packtmp);
80
81
+struct pack_geometry {
82
+ struct packed_git **pack;
83
+ uint32_t pack_nr, pack_alloc;
84
+ uint32_t split;
85
+
86
+ int split_factor;
87
+};
88
+
89
+void pack_geometry_init(struct pack_geometry *geometry,
90
+ struct existing_packs *existing,
91
+ const struct pack_objects_args *args,
92
+ int pack_kept_objects);
93
+void pack_geometry_split(struct pack_geometry *geometry);
94
+struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry);
95
+void pack_geometry_remove_redundant(struct pack_geometry *geometry,
96
+ struct string_list *names,
97
+ struct existing_packs *existing,
98
+ const char *packdir);
99
+void pack_geometry_release(struct pack_geometry *geometry);
100
+
101
#endif /* REPACK_H */