Raw
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 "midx.h"
8 #include "packfile.h"
9
10 static uint32_t pack_geometry_weight(struct packed_git *p)
11 {
12 if (open_pack_index(p))
13 die(_("cannot open index for %s"), p->pack_name);
14 return p->num_objects;
15 }
16
17 static int pack_geometry_cmp(const void *va, const void *vb)
18 {
19 uint32_t aw = pack_geometry_weight(*(struct packed_git **)va),
20 bw = pack_geometry_weight(*(struct packed_git **)vb);
21
22 if (aw < bw)
23 return -1;
24 if (aw > bw)
25 return 1;
26 return 0;
27 }
28
29 void pack_geometry_init(struct pack_geometry *geometry,
30 struct existing_packs *existing,
31 const struct pack_objects_args *args)
32 {
33 struct packed_git *p;
34 struct strbuf buf = STRBUF_INIT;
35 struct multi_pack_index *m = get_multi_pack_index(existing->source);
36
37 repo_for_each_pack(existing->repo, p) {
38 if (geometry->midx_layer_threshold_set && m &&
39 p->multi_pack_index) {
40 /*
41 * When writing MIDX layers incrementally,
42 * ignore packs unless they are in the most
43 * recent MIDX layer *and* there are at least
44 * 'midx_layer_threshold' packs in that layer.
45 *
46 * Otherwise 'p' is either in an older layer, or
47 * the youngest layer does not have enough packs
48 * to consider its packs as candidates for
49 * repacking. In either of those cases we want
50 * to ignore the pack.
51 */
52 if (m->num_packs < geometry->midx_layer_threshold ||
53 !midx_layer_contains_pack(m, pack_basename(p)))
54 continue;
55 }
56
57 if (args->local && !p->pack_local)
58 /*
59 * When asked to only repack local packfiles we skip
60 * over any packfiles that are borrowed from alternate
61 * object directories.
62 */
63 continue;
64
65 if (!args->pack_kept_objects) {
66 /*
67 * Any pack that has its pack_keep bit set will
68 * appear in existing->kept_packs below, but
69 * this saves us from doing a more expensive
70 * check.
71 */
72 if (p->pack_keep)
73 continue;
74
75 /*
76 * The pack may be kept via the --keep-pack
77 * option; check 'existing->kept_packs' to
78 * determine whether to ignore it.
79 */
80 strbuf_reset(&buf);
81 strbuf_addstr(&buf, pack_basename(p));
82 strbuf_strip_suffix(&buf, ".pack");
83
84 if (string_list_has_string(&existing->kept_packs, buf.buf))
85 continue;
86 }
87 if (p->is_cruft)
88 continue;
89
90 if (p->pack_promisor) {
91 ALLOC_GROW(geometry->promisor_pack,
92 geometry->promisor_pack_nr + 1,
93 geometry->promisor_pack_alloc);
94
95 geometry->promisor_pack[geometry->promisor_pack_nr] = p;
96 geometry->promisor_pack_nr++;
97 } else {
98 ALLOC_GROW(geometry->pack,
99 geometry->pack_nr + 1,
100 geometry->pack_alloc);
101
102 geometry->pack[geometry->pack_nr] = p;
103 geometry->pack_nr++;
104 }
105 }
106
107 QSORT(geometry->pack, geometry->pack_nr, pack_geometry_cmp);
108 QSORT(geometry->promisor_pack, geometry->promisor_pack_nr, pack_geometry_cmp);
109 strbuf_release(&buf);
110 }
111
112 static uint32_t compute_pack_geometry_split(struct packed_git **pack, size_t pack_nr,
113 int split_factor)
114 {
115 uint32_t i;
116 uint32_t split;
117 off_t total_size = 0;
118
119 if (!pack_nr)
120 return 0;
121
122 /*
123 * First, count the number of packs (in descending order of size) which
124 * already form a geometric progression.
125 */
126 for (i = pack_nr - 1; i > 0; i--) {
127 struct packed_git *ours = pack[i];
128 struct packed_git *prev = pack[i - 1];
129
130 if (unsigned_mult_overflows(split_factor,
131 pack_geometry_weight(prev)))
132 die(_("pack %s too large to consider in geometric "
133 "progression"),
134 prev->pack_name);
135
136 if (pack_geometry_weight(ours) <
137 split_factor * pack_geometry_weight(prev))
138 break;
139 }
140
141 split = i;
142
143 if (split) {
144 /*
145 * Move the split one to the right, since the top element in the
146 * last-compared pair can't be in the progression. Only do this
147 * when we split in the middle of the array (otherwise if we got
148 * to the end, then the split is in the right place).
149 */
150 split++;
151 }
152
153 /*
154 * Then, anything to the left of 'split' must be in a new pack. But,
155 * creating that new pack may cause packs in the heavy half to no longer
156 * form a geometric progression.
157 *
158 * Compute an expected size of the new pack, and then determine how many
159 * packs in the heavy half need to be joined into it (if any) to restore
160 * the geometric progression.
161 */
162 for (i = 0; i < split; i++) {
163 struct packed_git *p = pack[i];
164
165 if (unsigned_add_overflows(total_size, pack_geometry_weight(p)))
166 die(_("pack %s too large to roll up"), p->pack_name);
167 total_size += pack_geometry_weight(p);
168 }
169 for (i = split; i < pack_nr; i++) {
170 struct packed_git *ours = pack[i];
171
172 if (unsigned_mult_overflows(split_factor, total_size))
173 die(_("pack %s too large to roll up"), ours->pack_name);
174
175 if (pack_geometry_weight(ours) < split_factor * total_size) {
176 if (unsigned_add_overflows(total_size,
177 pack_geometry_weight(ours)))
178 die(_("pack %s too large to roll up"),
179 ours->pack_name);
180
181 split++;
182 total_size += pack_geometry_weight(ours);
183 } else
184 break;
185 }
186
187 return split;
188 }
189
190 void pack_geometry_split(struct pack_geometry *geometry)
191 {
192 geometry->split = compute_pack_geometry_split(geometry->pack, geometry->pack_nr,
193 geometry->split_factor);
194 geometry->promisor_split = compute_pack_geometry_split(geometry->promisor_pack,
195 geometry->promisor_pack_nr,
196 geometry->split_factor);
197 for (uint32_t i = 0; i < geometry->split; i++) {
198 struct packed_git *p = geometry->pack[i];
199 /*
200 * During incremental MIDX/bitmap repacking, any packs
201 * included in the rollup are either (a) not MIDX'd, or
202 * (b) contained in the tip layer iff it has at least
203 * the threshold number of packs.
204 *
205 * In the latter case, we can safely conclude that the
206 * tip of the MIDX chain will be rewritten.
207 */
208 if (p->multi_pack_index)
209 geometry->midx_tip_rewritten = true;
210 }
211 }
212
213 struct packed_git *pack_geometry_preferred_pack(struct pack_geometry *geometry)
214 {
215 uint32_t i;
216
217 if (!geometry) {
218 /*
219 * No geometry means either an all-into-one repack (in which
220 * case there is only one pack left and it is the largest) or an
221 * incremental one.
222 *
223 * If repacking incrementally, then we could check the size of
224 * all packs to determine which should be preferred, but leave
225 * this for later.
226 */
227 return NULL;
228 }
229 if (geometry->split == geometry->pack_nr)
230 return NULL;
231
232 /*
233 * The preferred pack is the largest pack above the split line. In
234 * other words, it is the largest pack that does not get rolled up in
235 * the geometric repack.
236 */
237 for (i = geometry->pack_nr; i > geometry->split; i--)
238 /*
239 * A pack that is not local would never be included in a
240 * multi-pack index. We thus skip over any non-local packs.
241 */
242 if (geometry->pack[i - 1]->pack_local)
243 return geometry->pack[i - 1];
244
245 return NULL;
246 }
247
248 static void remove_redundant_packs(struct packed_git **pack,
249 uint32_t pack_nr,
250 struct string_list *names,
251 struct existing_packs *existing,
252 const char *packdir,
253 bool wrote_incremental_midx)
254 {
255 const struct git_hash_algo *algop = existing->repo->hash_algo;
256 struct strbuf buf = STRBUF_INIT;
257 uint32_t i;
258
259 for (i = 0; i < pack_nr; i++) {
260 struct packed_git *p = pack[i];
261 if (string_list_has_string(names, hash_to_hex_algop(p->hash,
262 algop)))
263 continue;
264
265 strbuf_reset(&buf);
266 strbuf_addstr(&buf, pack_basename(p));
267 strbuf_strip_suffix(&buf, ".pack");
268
269 if ((p->pack_keep) ||
270 (string_list_has_string(&existing->kept_packs, buf.buf)))
271 continue;
272
273 repack_remove_redundant_pack(existing->repo, packdir, buf.buf,
274 wrote_incremental_midx);
275 }
276
277 strbuf_release(&buf);
278 }
279
280 void pack_geometry_remove_redundant(struct pack_geometry *geometry,
281 struct string_list *names,
282 struct existing_packs *existing,
283 const char *packdir,
284 bool wrote_incremental_midx)
285 {
286 remove_redundant_packs(geometry->pack, geometry->split,
287 names, existing, packdir, wrote_incremental_midx);
288 remove_redundant_packs(geometry->promisor_pack, geometry->promisor_split,
289 names, existing, packdir, wrote_incremental_midx);
290 }
291
292 void pack_geometry_release(struct pack_geometry *geometry)
293 {
294 if (!geometry)
295 return;
296
297 free(geometry->pack);
298 free(geometry->promisor_pack);
299 }