Line | Count | Source (jump to first uncovered line) |
1 | | #define USE_THE_REPOSITORY_VARIABLE |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "abspath.h" |
5 | | #include "config.h" |
6 | | #include "hex.h" |
7 | | #include "lockfile.h" |
8 | | #include "packfile.h" |
9 | | #include "object-file.h" |
10 | | #include "hash-lookup.h" |
11 | | #include "midx.h" |
12 | | #include "progress.h" |
13 | | #include "trace2.h" |
14 | | #include "run-command.h" |
15 | | #include "chunk-format.h" |
16 | | #include "pack-bitmap.h" |
17 | | #include "refs.h" |
18 | | #include "revision.h" |
19 | | #include "list-objects.h" |
20 | | #include "path.h" |
21 | | #include "pack-revindex.h" |
22 | | |
23 | 0 | #define PACK_EXPIRED UINT_MAX |
24 | 0 | #define BITMAP_POS_UNKNOWN (~((uint32_t)0)) |
25 | 0 | #define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256) |
26 | 0 | #define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t)) |
27 | | |
28 | | extern int midx_checksum_valid(struct multi_pack_index *m); |
29 | | extern void clear_midx_files_ext(const char *object_dir, const char *ext, |
30 | | const char *keep_hash); |
31 | | extern void clear_incremental_midx_files_ext(const char *object_dir, |
32 | | const char *ext, |
33 | | const char **keep_hashes, |
34 | | uint32_t hashes_nr); |
35 | | extern int cmp_idx_or_pack_name(const char *idx_or_pack_name, |
36 | | const char *idx_name); |
37 | | |
38 | | static size_t write_midx_header(struct hashfile *f, |
39 | | unsigned char num_chunks, |
40 | | uint32_t num_packs) |
41 | 0 | { |
42 | 0 | hashwrite_be32(f, MIDX_SIGNATURE); |
43 | 0 | hashwrite_u8(f, MIDX_VERSION); |
44 | 0 | hashwrite_u8(f, oid_version(the_hash_algo)); |
45 | 0 | hashwrite_u8(f, num_chunks); |
46 | 0 | hashwrite_u8(f, 0); /* unused */ |
47 | 0 | hashwrite_be32(f, num_packs); |
48 | |
|
49 | 0 | return MIDX_HEADER_SIZE; |
50 | 0 | } |
51 | | |
52 | | struct pack_info { |
53 | | uint32_t orig_pack_int_id; |
54 | | char *pack_name; |
55 | | struct packed_git *p; |
56 | | |
57 | | uint32_t bitmap_pos; |
58 | | uint32_t bitmap_nr; |
59 | | |
60 | | unsigned expired : 1; |
61 | | }; |
62 | | |
63 | | static void fill_pack_info(struct pack_info *info, |
64 | | struct packed_git *p, const char *pack_name, |
65 | | uint32_t orig_pack_int_id) |
66 | 0 | { |
67 | 0 | memset(info, 0, sizeof(struct pack_info)); |
68 | |
|
69 | 0 | info->orig_pack_int_id = orig_pack_int_id; |
70 | 0 | info->pack_name = xstrdup(pack_name); |
71 | 0 | info->p = p; |
72 | 0 | info->bitmap_pos = BITMAP_POS_UNKNOWN; |
73 | 0 | } |
74 | | |
75 | | static int pack_info_compare(const void *_a, const void *_b) |
76 | 0 | { |
77 | 0 | struct pack_info *a = (struct pack_info *)_a; |
78 | 0 | struct pack_info *b = (struct pack_info *)_b; |
79 | 0 | return strcmp(a->pack_name, b->pack_name); |
80 | 0 | } |
81 | | |
82 | | static int idx_or_pack_name_cmp(const void *_va, const void *_vb) |
83 | 0 | { |
84 | 0 | const char *pack_name = _va; |
85 | 0 | const struct pack_info *compar = _vb; |
86 | |
|
87 | 0 | return cmp_idx_or_pack_name(pack_name, compar->pack_name); |
88 | 0 | } |
89 | | |
90 | | struct write_midx_context { |
91 | | struct pack_info *info; |
92 | | size_t nr; |
93 | | size_t alloc; |
94 | | struct multi_pack_index *m; |
95 | | struct multi_pack_index *base_midx; |
96 | | struct progress *progress; |
97 | | unsigned pack_paths_checked; |
98 | | |
99 | | struct pack_midx_entry *entries; |
100 | | size_t entries_nr; |
101 | | |
102 | | uint32_t *pack_perm; |
103 | | uint32_t *pack_order; |
104 | | unsigned large_offsets_needed:1; |
105 | | uint32_t num_large_offsets; |
106 | | |
107 | | int preferred_pack_idx; |
108 | | |
109 | | int incremental; |
110 | | uint32_t num_multi_pack_indexes_before; |
111 | | |
112 | | struct string_list *to_include; |
113 | | }; |
114 | | |
115 | | static int should_include_pack(const struct write_midx_context *ctx, |
116 | | const char *file_name) |
117 | 0 | { |
118 | | /* |
119 | | * Note that at most one of ctx->m and ctx->to_include are set, |
120 | | * so we are testing midx_contains_pack() and |
121 | | * string_list_has_string() independently (guarded by the |
122 | | * appropriate NULL checks). |
123 | | * |
124 | | * We could support passing to_include while reusing an existing |
125 | | * MIDX, but don't currently since the reuse process drags |
126 | | * forward all packs from an existing MIDX (without checking |
127 | | * whether or not they appear in the to_include list). |
128 | | * |
129 | | * If we added support for that, these next two conditional |
130 | | * should be performed independently (likely checking |
131 | | * to_include before the existing MIDX). |
132 | | */ |
133 | 0 | if (ctx->m && midx_contains_pack(ctx->m, file_name)) |
134 | 0 | return 0; |
135 | 0 | else if (ctx->base_midx && midx_contains_pack(ctx->base_midx, |
136 | 0 | file_name)) |
137 | 0 | return 0; |
138 | 0 | else if (ctx->to_include && |
139 | 0 | !string_list_has_string(ctx->to_include, file_name)) |
140 | 0 | return 0; |
141 | 0 | return 1; |
142 | 0 | } |
143 | | |
144 | | static void add_pack_to_midx(const char *full_path, size_t full_path_len, |
145 | | const char *file_name, void *data) |
146 | 0 | { |
147 | 0 | struct write_midx_context *ctx = data; |
148 | 0 | struct packed_git *p; |
149 | |
|
150 | 0 | if (ends_with(file_name, ".idx")) { |
151 | 0 | display_progress(ctx->progress, ++ctx->pack_paths_checked); |
152 | |
|
153 | 0 | if (!should_include_pack(ctx, file_name)) |
154 | 0 | return; |
155 | | |
156 | 0 | ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc); |
157 | 0 | p = add_packed_git(full_path, full_path_len, 0); |
158 | 0 | if (!p) { |
159 | 0 | warning(_("failed to add packfile '%s'"), |
160 | 0 | full_path); |
161 | 0 | return; |
162 | 0 | } |
163 | | |
164 | 0 | if (open_pack_index(p)) { |
165 | 0 | warning(_("failed to open pack-index '%s'"), |
166 | 0 | full_path); |
167 | 0 | close_pack(p); |
168 | 0 | free(p); |
169 | 0 | return; |
170 | 0 | } |
171 | | |
172 | 0 | fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr); |
173 | 0 | ctx->nr++; |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | struct pack_midx_entry { |
178 | | struct object_id oid; |
179 | | uint32_t pack_int_id; |
180 | | time_t pack_mtime; |
181 | | uint64_t offset; |
182 | | unsigned preferred : 1; |
183 | | }; |
184 | | |
185 | | static int midx_oid_compare(const void *_a, const void *_b) |
186 | 0 | { |
187 | 0 | const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a; |
188 | 0 | const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b; |
189 | 0 | int cmp = oidcmp(&a->oid, &b->oid); |
190 | |
|
191 | 0 | if (cmp) |
192 | 0 | return cmp; |
193 | | |
194 | | /* Sort objects in a preferred pack first when multiple copies exist. */ |
195 | 0 | if (a->preferred > b->preferred) |
196 | 0 | return -1; |
197 | 0 | if (a->preferred < b->preferred) |
198 | 0 | return 1; |
199 | | |
200 | 0 | if (a->pack_mtime > b->pack_mtime) |
201 | 0 | return -1; |
202 | 0 | else if (a->pack_mtime < b->pack_mtime) |
203 | 0 | return 1; |
204 | | |
205 | 0 | return a->pack_int_id - b->pack_int_id; |
206 | 0 | } |
207 | | |
208 | | static int nth_midxed_pack_midx_entry(struct multi_pack_index *m, |
209 | | struct pack_midx_entry *e, |
210 | | uint32_t pos) |
211 | 0 | { |
212 | 0 | if (pos >= m->num_objects + m->num_objects_in_base) |
213 | 0 | return 1; |
214 | | |
215 | 0 | nth_midxed_object_oid(&e->oid, m, pos); |
216 | 0 | e->pack_int_id = nth_midxed_pack_int_id(m, pos); |
217 | 0 | e->offset = nth_midxed_offset(m, pos); |
218 | | |
219 | | /* consider objects in midx to be from "old" packs */ |
220 | 0 | e->pack_mtime = 0; |
221 | 0 | return 0; |
222 | 0 | } |
223 | | |
224 | | static void fill_pack_entry(uint32_t pack_int_id, |
225 | | struct packed_git *p, |
226 | | uint32_t cur_object, |
227 | | struct pack_midx_entry *entry, |
228 | | int preferred) |
229 | 0 | { |
230 | 0 | if (nth_packed_object_id(&entry->oid, p, cur_object) < 0) |
231 | 0 | die(_("failed to locate object %d in packfile"), cur_object); |
232 | | |
233 | 0 | entry->pack_int_id = pack_int_id; |
234 | 0 | entry->pack_mtime = p->mtime; |
235 | |
|
236 | 0 | entry->offset = nth_packed_object_offset(p, cur_object); |
237 | 0 | entry->preferred = !!preferred; |
238 | 0 | } |
239 | | |
240 | | struct midx_fanout { |
241 | | struct pack_midx_entry *entries; |
242 | | size_t nr, alloc; |
243 | | }; |
244 | | |
245 | | static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr) |
246 | 0 | { |
247 | 0 | if (nr < fanout->nr) |
248 | 0 | BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")", |
249 | 0 | (uintmax_t)nr, (uintmax_t)fanout->nr); |
250 | 0 | ALLOC_GROW(fanout->entries, nr, fanout->alloc); |
251 | 0 | } |
252 | | |
253 | | static void midx_fanout_sort(struct midx_fanout *fanout) |
254 | 0 | { |
255 | 0 | QSORT(fanout->entries, fanout->nr, midx_oid_compare); |
256 | 0 | } |
257 | | |
258 | | static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, |
259 | | struct multi_pack_index *m, |
260 | | uint32_t cur_fanout, |
261 | | int preferred_pack) |
262 | 0 | { |
263 | 0 | uint32_t start = m->num_objects_in_base, end; |
264 | 0 | uint32_t cur_object; |
265 | |
|
266 | 0 | if (m->base_midx) |
267 | 0 | midx_fanout_add_midx_fanout(fanout, m->base_midx, cur_fanout, |
268 | 0 | preferred_pack); |
269 | |
|
270 | 0 | if (cur_fanout) |
271 | 0 | start += ntohl(m->chunk_oid_fanout[cur_fanout - 1]); |
272 | 0 | end = m->num_objects_in_base + ntohl(m->chunk_oid_fanout[cur_fanout]); |
273 | |
|
274 | 0 | for (cur_object = start; cur_object < end; cur_object++) { |
275 | 0 | if ((preferred_pack > -1) && |
276 | 0 | (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) { |
277 | | /* |
278 | | * Objects from preferred packs are added |
279 | | * separately. |
280 | | */ |
281 | 0 | continue; |
282 | 0 | } |
283 | | |
284 | 0 | midx_fanout_grow(fanout, fanout->nr + 1); |
285 | 0 | nth_midxed_pack_midx_entry(m, |
286 | 0 | &fanout->entries[fanout->nr], |
287 | 0 | cur_object); |
288 | 0 | fanout->entries[fanout->nr].preferred = 0; |
289 | 0 | fanout->nr++; |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout, |
294 | | struct pack_info *info, |
295 | | uint32_t cur_pack, |
296 | | int preferred, |
297 | | uint32_t cur_fanout) |
298 | 0 | { |
299 | 0 | struct packed_git *pack = info[cur_pack].p; |
300 | 0 | uint32_t start = 0, end; |
301 | 0 | uint32_t cur_object; |
302 | |
|
303 | 0 | if (cur_fanout) |
304 | 0 | start = get_pack_fanout(pack, cur_fanout - 1); |
305 | 0 | end = get_pack_fanout(pack, cur_fanout); |
306 | |
|
307 | 0 | for (cur_object = start; cur_object < end; cur_object++) { |
308 | 0 | midx_fanout_grow(fanout, fanout->nr + 1); |
309 | 0 | fill_pack_entry(cur_pack, |
310 | 0 | info[cur_pack].p, |
311 | 0 | cur_object, |
312 | 0 | &fanout->entries[fanout->nr], |
313 | 0 | preferred); |
314 | 0 | fanout->nr++; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | /* |
319 | | * It is possible to artificially get into a state where there are many |
320 | | * duplicate copies of objects. That can create high memory pressure if |
321 | | * we are to create a list of all objects before de-duplication. To reduce |
322 | | * this memory pressure without a significant performance drop, automatically |
323 | | * group objects by the first byte of their object id. Use the IDX fanout |
324 | | * tables to group the data, copy to a local array, then sort. |
325 | | * |
326 | | * Copy only the de-duplicated entries (selected by most-recent modified time |
327 | | * of a packfile containing the object). |
328 | | */ |
329 | | static void compute_sorted_entries(struct write_midx_context *ctx, |
330 | | uint32_t start_pack) |
331 | 0 | { |
332 | 0 | uint32_t cur_fanout, cur_pack, cur_object; |
333 | 0 | size_t alloc_objects, total_objects = 0; |
334 | 0 | struct midx_fanout fanout = { 0 }; |
335 | |
|
336 | 0 | for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) |
337 | 0 | total_objects = st_add(total_objects, |
338 | 0 | ctx->info[cur_pack].p->num_objects); |
339 | | |
340 | | /* |
341 | | * As we de-duplicate by fanout value, we expect the fanout |
342 | | * slices to be evenly distributed, with some noise. Hence, |
343 | | * allocate slightly more than one 256th. |
344 | | */ |
345 | 0 | alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16; |
346 | |
|
347 | 0 | ALLOC_ARRAY(fanout.entries, fanout.alloc); |
348 | 0 | ALLOC_ARRAY(ctx->entries, alloc_objects); |
349 | 0 | ctx->entries_nr = 0; |
350 | |
|
351 | 0 | for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) { |
352 | 0 | fanout.nr = 0; |
353 | |
|
354 | 0 | if (ctx->m && !ctx->incremental) |
355 | 0 | midx_fanout_add_midx_fanout(&fanout, ctx->m, cur_fanout, |
356 | 0 | ctx->preferred_pack_idx); |
357 | |
|
358 | 0 | for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) { |
359 | 0 | int preferred = cur_pack == ctx->preferred_pack_idx; |
360 | 0 | midx_fanout_add_pack_fanout(&fanout, |
361 | 0 | ctx->info, cur_pack, |
362 | 0 | preferred, cur_fanout); |
363 | 0 | } |
364 | |
|
365 | 0 | if (-1 < ctx->preferred_pack_idx && ctx->preferred_pack_idx < start_pack) |
366 | 0 | midx_fanout_add_pack_fanout(&fanout, ctx->info, |
367 | 0 | ctx->preferred_pack_idx, 1, |
368 | 0 | cur_fanout); |
369 | |
|
370 | 0 | midx_fanout_sort(&fanout); |
371 | | |
372 | | /* |
373 | | * The batch is now sorted by OID and then mtime (descending). |
374 | | * Take only the first duplicate. |
375 | | */ |
376 | 0 | for (cur_object = 0; cur_object < fanout.nr; cur_object++) { |
377 | 0 | if (cur_object && oideq(&fanout.entries[cur_object - 1].oid, |
378 | 0 | &fanout.entries[cur_object].oid)) |
379 | 0 | continue; |
380 | 0 | if (ctx->incremental && ctx->base_midx && |
381 | 0 | midx_has_oid(ctx->base_midx, |
382 | 0 | &fanout.entries[cur_object].oid)) |
383 | 0 | continue; |
384 | | |
385 | 0 | ALLOC_GROW(ctx->entries, st_add(ctx->entries_nr, 1), |
386 | 0 | alloc_objects); |
387 | 0 | memcpy(&ctx->entries[ctx->entries_nr], |
388 | 0 | &fanout.entries[cur_object], |
389 | 0 | sizeof(struct pack_midx_entry)); |
390 | 0 | ctx->entries_nr++; |
391 | 0 | } |
392 | 0 | } |
393 | |
|
394 | 0 | free(fanout.entries); |
395 | 0 | } |
396 | | |
397 | | static int write_midx_pack_names(struct hashfile *f, void *data) |
398 | 0 | { |
399 | 0 | struct write_midx_context *ctx = data; |
400 | 0 | uint32_t i; |
401 | 0 | unsigned char padding[MIDX_CHUNK_ALIGNMENT]; |
402 | 0 | size_t written = 0; |
403 | |
|
404 | 0 | for (i = 0; i < ctx->nr; i++) { |
405 | 0 | size_t writelen; |
406 | |
|
407 | 0 | if (ctx->info[i].expired) |
408 | 0 | continue; |
409 | | |
410 | 0 | if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0) |
411 | 0 | BUG("incorrect pack-file order: %s before %s", |
412 | 0 | ctx->info[i - 1].pack_name, |
413 | 0 | ctx->info[i].pack_name); |
414 | | |
415 | 0 | writelen = strlen(ctx->info[i].pack_name) + 1; |
416 | 0 | hashwrite(f, ctx->info[i].pack_name, writelen); |
417 | 0 | written += writelen; |
418 | 0 | } |
419 | | |
420 | | /* add padding to be aligned */ |
421 | 0 | i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT); |
422 | 0 | if (i < MIDX_CHUNK_ALIGNMENT) { |
423 | 0 | memset(padding, 0, sizeof(padding)); |
424 | 0 | hashwrite(f, padding, i); |
425 | 0 | } |
426 | |
|
427 | 0 | return 0; |
428 | 0 | } |
429 | | |
430 | | static int write_midx_bitmapped_packs(struct hashfile *f, void *data) |
431 | 0 | { |
432 | 0 | struct write_midx_context *ctx = data; |
433 | 0 | size_t i; |
434 | |
|
435 | 0 | for (i = 0; i < ctx->nr; i++) { |
436 | 0 | struct pack_info *pack = &ctx->info[i]; |
437 | 0 | if (pack->expired) |
438 | 0 | continue; |
439 | | |
440 | 0 | if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr) |
441 | 0 | BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)", |
442 | 0 | pack->pack_name, pack->bitmap_nr); |
443 | | |
444 | 0 | hashwrite_be32(f, pack->bitmap_pos); |
445 | 0 | hashwrite_be32(f, pack->bitmap_nr); |
446 | 0 | } |
447 | 0 | return 0; |
448 | 0 | } |
449 | | |
450 | | static int write_midx_oid_fanout(struct hashfile *f, |
451 | | void *data) |
452 | 0 | { |
453 | 0 | struct write_midx_context *ctx = data; |
454 | 0 | struct pack_midx_entry *list = ctx->entries; |
455 | 0 | struct pack_midx_entry *last = ctx->entries + ctx->entries_nr; |
456 | 0 | uint32_t count = 0; |
457 | 0 | uint32_t i; |
458 | | |
459 | | /* |
460 | | * Write the first-level table (the list is sorted, |
461 | | * but we use a 256-entry lookup to be able to avoid |
462 | | * having to do eight extra binary search iterations). |
463 | | */ |
464 | 0 | for (i = 0; i < 256; i++) { |
465 | 0 | struct pack_midx_entry *next = list; |
466 | |
|
467 | 0 | while (next < last && next->oid.hash[0] == i) { |
468 | 0 | count++; |
469 | 0 | next++; |
470 | 0 | } |
471 | |
|
472 | 0 | hashwrite_be32(f, count); |
473 | 0 | list = next; |
474 | 0 | } |
475 | |
|
476 | 0 | return 0; |
477 | 0 | } |
478 | | |
479 | | static int write_midx_oid_lookup(struct hashfile *f, |
480 | | void *data) |
481 | 0 | { |
482 | 0 | struct write_midx_context *ctx = data; |
483 | 0 | unsigned char hash_len = the_hash_algo->rawsz; |
484 | 0 | struct pack_midx_entry *list = ctx->entries; |
485 | 0 | uint32_t i; |
486 | |
|
487 | 0 | for (i = 0; i < ctx->entries_nr; i++) { |
488 | 0 | struct pack_midx_entry *obj = list++; |
489 | |
|
490 | 0 | if (i < ctx->entries_nr - 1) { |
491 | 0 | struct pack_midx_entry *next = list; |
492 | 0 | if (oidcmp(&obj->oid, &next->oid) >= 0) |
493 | 0 | BUG("OIDs not in order: %s >= %s", |
494 | 0 | oid_to_hex(&obj->oid), |
495 | 0 | oid_to_hex(&next->oid)); |
496 | 0 | } |
497 | | |
498 | 0 | hashwrite(f, obj->oid.hash, (int)hash_len); |
499 | 0 | } |
500 | | |
501 | 0 | return 0; |
502 | 0 | } |
503 | | |
504 | | static int write_midx_object_offsets(struct hashfile *f, |
505 | | void *data) |
506 | 0 | { |
507 | 0 | struct write_midx_context *ctx = data; |
508 | 0 | struct pack_midx_entry *list = ctx->entries; |
509 | 0 | uint32_t i, nr_large_offset = 0; |
510 | |
|
511 | 0 | for (i = 0; i < ctx->entries_nr; i++) { |
512 | 0 | struct pack_midx_entry *obj = list++; |
513 | |
|
514 | 0 | if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED) |
515 | 0 | BUG("object %s is in an expired pack with int-id %d", |
516 | 0 | oid_to_hex(&obj->oid), |
517 | 0 | obj->pack_int_id); |
518 | | |
519 | 0 | hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]); |
520 | |
|
521 | 0 | if (ctx->large_offsets_needed && obj->offset >> 31) |
522 | 0 | hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++); |
523 | 0 | else if (!ctx->large_offsets_needed && obj->offset >> 32) |
524 | 0 | BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!", |
525 | 0 | oid_to_hex(&obj->oid), |
526 | 0 | obj->offset); |
527 | 0 | else |
528 | 0 | hashwrite_be32(f, (uint32_t)obj->offset); |
529 | 0 | } |
530 | | |
531 | 0 | return 0; |
532 | 0 | } |
533 | | |
534 | | static int write_midx_large_offsets(struct hashfile *f, |
535 | | void *data) |
536 | 0 | { |
537 | 0 | struct write_midx_context *ctx = data; |
538 | 0 | struct pack_midx_entry *list = ctx->entries; |
539 | 0 | struct pack_midx_entry *end = ctx->entries + ctx->entries_nr; |
540 | 0 | uint32_t nr_large_offset = ctx->num_large_offsets; |
541 | |
|
542 | 0 | while (nr_large_offset) { |
543 | 0 | struct pack_midx_entry *obj; |
544 | 0 | uint64_t offset; |
545 | |
|
546 | 0 | if (list >= end) |
547 | 0 | BUG("too many large-offset objects"); |
548 | | |
549 | 0 | obj = list++; |
550 | 0 | offset = obj->offset; |
551 | |
|
552 | 0 | if (!(offset >> 31)) |
553 | 0 | continue; |
554 | | |
555 | 0 | hashwrite_be64(f, offset); |
556 | |
|
557 | 0 | nr_large_offset--; |
558 | 0 | } |
559 | | |
560 | 0 | return 0; |
561 | 0 | } |
562 | | |
563 | | static int write_midx_revindex(struct hashfile *f, |
564 | | void *data) |
565 | 0 | { |
566 | 0 | struct write_midx_context *ctx = data; |
567 | 0 | uint32_t i, nr_base; |
568 | |
|
569 | 0 | if (ctx->incremental && ctx->base_midx) |
570 | 0 | nr_base = ctx->base_midx->num_objects + |
571 | 0 | ctx->base_midx->num_objects_in_base; |
572 | 0 | else |
573 | 0 | nr_base = 0; |
574 | |
|
575 | 0 | for (i = 0; i < ctx->entries_nr; i++) |
576 | 0 | hashwrite_be32(f, ctx->pack_order[i] + nr_base); |
577 | |
|
578 | 0 | return 0; |
579 | 0 | } |
580 | | |
581 | | struct midx_pack_order_data { |
582 | | uint32_t nr; |
583 | | uint32_t pack; |
584 | | off_t offset; |
585 | | }; |
586 | | |
587 | | static int midx_pack_order_cmp(const void *va, const void *vb) |
588 | 0 | { |
589 | 0 | const struct midx_pack_order_data *a = va, *b = vb; |
590 | 0 | if (a->pack < b->pack) |
591 | 0 | return -1; |
592 | 0 | else if (a->pack > b->pack) |
593 | 0 | return 1; |
594 | 0 | else if (a->offset < b->offset) |
595 | 0 | return -1; |
596 | 0 | else if (a->offset > b->offset) |
597 | 0 | return 1; |
598 | 0 | else |
599 | 0 | return 0; |
600 | 0 | } |
601 | | |
602 | | static uint32_t *midx_pack_order(struct write_midx_context *ctx) |
603 | 0 | { |
604 | 0 | struct midx_pack_order_data *data; |
605 | 0 | uint32_t *pack_order, base_objects = 0; |
606 | 0 | uint32_t i; |
607 | |
|
608 | 0 | trace2_region_enter("midx", "midx_pack_order", the_repository); |
609 | |
|
610 | 0 | if (ctx->incremental && ctx->base_midx) |
611 | 0 | base_objects = ctx->base_midx->num_objects + |
612 | 0 | ctx->base_midx->num_objects_in_base; |
613 | |
|
614 | 0 | ALLOC_ARRAY(pack_order, ctx->entries_nr); |
615 | 0 | ALLOC_ARRAY(data, ctx->entries_nr); |
616 | |
|
617 | 0 | for (i = 0; i < ctx->entries_nr; i++) { |
618 | 0 | struct pack_midx_entry *e = &ctx->entries[i]; |
619 | 0 | data[i].nr = i; |
620 | 0 | data[i].pack = ctx->pack_perm[e->pack_int_id]; |
621 | 0 | if (!e->preferred) |
622 | 0 | data[i].pack |= (1U << 31); |
623 | 0 | data[i].offset = e->offset; |
624 | 0 | } |
625 | |
|
626 | 0 | QSORT(data, ctx->entries_nr, midx_pack_order_cmp); |
627 | |
|
628 | 0 | for (i = 0; i < ctx->entries_nr; i++) { |
629 | 0 | struct pack_midx_entry *e = &ctx->entries[data[i].nr]; |
630 | 0 | struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]]; |
631 | 0 | if (pack->bitmap_pos == BITMAP_POS_UNKNOWN) |
632 | 0 | pack->bitmap_pos = i + base_objects; |
633 | 0 | pack->bitmap_nr++; |
634 | 0 | pack_order[i] = data[i].nr; |
635 | 0 | } |
636 | 0 | for (i = 0; i < ctx->nr; i++) { |
637 | 0 | struct pack_info *pack = &ctx->info[ctx->pack_perm[i]]; |
638 | 0 | if (pack->bitmap_pos == BITMAP_POS_UNKNOWN) |
639 | 0 | pack->bitmap_pos = 0; |
640 | 0 | } |
641 | 0 | free(data); |
642 | |
|
643 | 0 | trace2_region_leave("midx", "midx_pack_order", the_repository); |
644 | |
|
645 | 0 | return pack_order; |
646 | 0 | } |
647 | | |
648 | | static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash, |
649 | | struct write_midx_context *ctx) |
650 | 0 | { |
651 | 0 | struct strbuf buf = STRBUF_INIT; |
652 | 0 | const char *tmp_file; |
653 | |
|
654 | 0 | trace2_region_enter("midx", "write_midx_reverse_index", the_repository); |
655 | |
|
656 | 0 | strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash)); |
657 | |
|
658 | 0 | tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr, |
659 | 0 | midx_hash, WRITE_REV); |
660 | |
|
661 | 0 | if (finalize_object_file(tmp_file, buf.buf)) |
662 | 0 | die(_("cannot store reverse index file")); |
663 | | |
664 | 0 | strbuf_release(&buf); |
665 | |
|
666 | 0 | trace2_region_leave("midx", "write_midx_reverse_index", the_repository); |
667 | 0 | } |
668 | | |
669 | | static void prepare_midx_packing_data(struct packing_data *pdata, |
670 | | struct write_midx_context *ctx) |
671 | 0 | { |
672 | 0 | uint32_t i; |
673 | |
|
674 | 0 | trace2_region_enter("midx", "prepare_midx_packing_data", the_repository); |
675 | |
|
676 | 0 | memset(pdata, 0, sizeof(struct packing_data)); |
677 | 0 | prepare_packing_data(the_repository, pdata); |
678 | |
|
679 | 0 | for (i = 0; i < ctx->entries_nr; i++) { |
680 | 0 | uint32_t pos = ctx->pack_order[i]; |
681 | 0 | struct pack_midx_entry *from = &ctx->entries[pos]; |
682 | 0 | struct object_entry *to = packlist_alloc(pdata, &from->oid); |
683 | |
|
684 | 0 | oe_set_in_pack(pdata, to, |
685 | 0 | ctx->info[ctx->pack_perm[from->pack_int_id]].p); |
686 | 0 | } |
687 | |
|
688 | 0 | trace2_region_leave("midx", "prepare_midx_packing_data", the_repository); |
689 | 0 | } |
690 | | |
691 | | static int add_ref_to_pending(const char *refname, const char *referent UNUSED, |
692 | | const struct object_id *oid, |
693 | | int flag, void *cb_data) |
694 | 0 | { |
695 | 0 | struct rev_info *revs = (struct rev_info*)cb_data; |
696 | 0 | struct object_id peeled; |
697 | 0 | struct object *object; |
698 | |
|
699 | 0 | if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) { |
700 | 0 | warning("symbolic ref is dangling: %s", refname); |
701 | 0 | return 0; |
702 | 0 | } |
703 | | |
704 | 0 | if (!peel_iterated_oid(the_repository, oid, &peeled)) |
705 | 0 | oid = &peeled; |
706 | |
|
707 | 0 | object = parse_object_or_die(oid, refname); |
708 | 0 | if (object->type != OBJ_COMMIT) |
709 | 0 | return 0; |
710 | | |
711 | 0 | add_pending_object(revs, object, ""); |
712 | 0 | if (bitmap_is_preferred_refname(revs->repo, refname)) |
713 | 0 | object->flags |= NEEDS_BITMAP; |
714 | 0 | return 0; |
715 | 0 | } |
716 | | |
717 | | struct bitmap_commit_cb { |
718 | | struct commit **commits; |
719 | | size_t commits_nr, commits_alloc; |
720 | | |
721 | | struct write_midx_context *ctx; |
722 | | }; |
723 | | |
724 | | static const struct object_id *bitmap_oid_access(size_t index, |
725 | | const void *_entries) |
726 | 0 | { |
727 | 0 | const struct pack_midx_entry *entries = _entries; |
728 | 0 | return &entries[index].oid; |
729 | 0 | } |
730 | | |
731 | | static void bitmap_show_commit(struct commit *commit, void *_data) |
732 | 0 | { |
733 | 0 | struct bitmap_commit_cb *data = _data; |
734 | 0 | int pos = oid_pos(&commit->object.oid, data->ctx->entries, |
735 | 0 | data->ctx->entries_nr, |
736 | 0 | bitmap_oid_access); |
737 | 0 | if (pos < 0) |
738 | 0 | return; |
739 | | |
740 | 0 | ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc); |
741 | 0 | data->commits[data->commits_nr++] = commit; |
742 | 0 | } |
743 | | |
744 | | static int read_refs_snapshot(const char *refs_snapshot, |
745 | | struct rev_info *revs) |
746 | 0 | { |
747 | 0 | struct strbuf buf = STRBUF_INIT; |
748 | 0 | struct object_id oid; |
749 | 0 | FILE *f = xfopen(refs_snapshot, "r"); |
750 | |
|
751 | 0 | while (strbuf_getline(&buf, f) != EOF) { |
752 | 0 | struct object *object; |
753 | 0 | int preferred = 0; |
754 | 0 | char *hex = buf.buf; |
755 | 0 | const char *end = NULL; |
756 | |
|
757 | 0 | if (buf.len && *buf.buf == '+') { |
758 | 0 | preferred = 1; |
759 | 0 | hex = &buf.buf[1]; |
760 | 0 | } |
761 | |
|
762 | 0 | if (parse_oid_hex(hex, &oid, &end) < 0) |
763 | 0 | die(_("could not parse line: %s"), buf.buf); |
764 | 0 | if (*end) |
765 | 0 | die(_("malformed line: %s"), buf.buf); |
766 | | |
767 | 0 | object = parse_object_or_die(&oid, NULL); |
768 | 0 | if (preferred) |
769 | 0 | object->flags |= NEEDS_BITMAP; |
770 | |
|
771 | 0 | add_pending_object(revs, object, ""); |
772 | 0 | } |
773 | | |
774 | 0 | fclose(f); |
775 | 0 | strbuf_release(&buf); |
776 | 0 | return 0; |
777 | 0 | } |
778 | | static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p, |
779 | | const char *refs_snapshot, |
780 | | struct write_midx_context *ctx) |
781 | 0 | { |
782 | 0 | struct rev_info revs; |
783 | 0 | struct bitmap_commit_cb cb = {0}; |
784 | |
|
785 | 0 | trace2_region_enter("midx", "find_commits_for_midx_bitmap", |
786 | 0 | the_repository); |
787 | |
|
788 | 0 | cb.ctx = ctx; |
789 | |
|
790 | 0 | repo_init_revisions(the_repository, &revs, NULL); |
791 | 0 | if (refs_snapshot) { |
792 | 0 | read_refs_snapshot(refs_snapshot, &revs); |
793 | 0 | } else { |
794 | 0 | setup_revisions(0, NULL, &revs, NULL); |
795 | 0 | refs_for_each_ref(get_main_ref_store(the_repository), |
796 | 0 | add_ref_to_pending, &revs); |
797 | 0 | } |
798 | | |
799 | | /* |
800 | | * Skipping promisor objects here is intentional, since it only excludes |
801 | | * them from the list of reachable commits that we want to select from |
802 | | * when computing the selection of MIDX'd commits to receive bitmaps. |
803 | | * |
804 | | * Reachability bitmaps do require that their objects be closed under |
805 | | * reachability, but fetching any objects missing from promisors at this |
806 | | * point is too late. But, if one of those objects can be reached from |
807 | | * an another object that is included in the bitmap, then we will |
808 | | * complain later that we don't have reachability closure (and fail |
809 | | * appropriately). |
810 | | */ |
811 | 0 | fetch_if_missing = 0; |
812 | 0 | revs.exclude_promisor_objects = 1; |
813 | |
|
814 | 0 | if (prepare_revision_walk(&revs)) |
815 | 0 | die(_("revision walk setup failed")); |
816 | | |
817 | 0 | traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb); |
818 | 0 | if (indexed_commits_nr_p) |
819 | 0 | *indexed_commits_nr_p = cb.commits_nr; |
820 | |
|
821 | 0 | release_revisions(&revs); |
822 | |
|
823 | 0 | trace2_region_leave("midx", "find_commits_for_midx_bitmap", |
824 | 0 | the_repository); |
825 | |
|
826 | 0 | return cb.commits; |
827 | 0 | } |
828 | | |
829 | | static int write_midx_bitmap(const char *midx_name, |
830 | | const unsigned char *midx_hash, |
831 | | struct packing_data *pdata, |
832 | | struct commit **commits, |
833 | | uint32_t commits_nr, |
834 | | uint32_t *pack_order, |
835 | | unsigned flags) |
836 | 0 | { |
837 | 0 | int ret, i; |
838 | 0 | uint16_t options = 0; |
839 | 0 | struct bitmap_writer writer; |
840 | 0 | struct pack_idx_entry **index; |
841 | 0 | char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name, |
842 | 0 | hash_to_hex(midx_hash)); |
843 | |
|
844 | 0 | trace2_region_enter("midx", "write_midx_bitmap", the_repository); |
845 | |
|
846 | 0 | if (flags & MIDX_WRITE_BITMAP_HASH_CACHE) |
847 | 0 | options |= BITMAP_OPT_HASH_CACHE; |
848 | |
|
849 | 0 | if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE) |
850 | 0 | options |= BITMAP_OPT_LOOKUP_TABLE; |
851 | | |
852 | | /* |
853 | | * Build the MIDX-order index based on pdata.objects (which is already |
854 | | * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of |
855 | | * this order). |
856 | | */ |
857 | 0 | ALLOC_ARRAY(index, pdata->nr_objects); |
858 | 0 | for (i = 0; i < pdata->nr_objects; i++) |
859 | 0 | index[i] = &pdata->objects[i].idx; |
860 | |
|
861 | 0 | bitmap_writer_init(&writer, the_repository, pdata); |
862 | 0 | bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS); |
863 | 0 | bitmap_writer_build_type_index(&writer, index); |
864 | | |
865 | | /* |
866 | | * bitmap_writer_finish expects objects in lex order, but pack_order |
867 | | * gives us exactly that. use it directly instead of re-sorting the |
868 | | * array. |
869 | | * |
870 | | * This changes the order of objects in 'index' between |
871 | | * bitmap_writer_build_type_index and bitmap_writer_finish. |
872 | | * |
873 | | * The same re-ordering takes place in the single-pack bitmap code via |
874 | | * write_idx_file(), which is called by finish_tmp_packfile(), which |
875 | | * happens between bitmap_writer_build_type_index() and |
876 | | * bitmap_writer_finish(). |
877 | | */ |
878 | 0 | for (i = 0; i < pdata->nr_objects; i++) |
879 | 0 | index[pack_order[i]] = &pdata->objects[i].idx; |
880 | |
|
881 | 0 | bitmap_writer_select_commits(&writer, commits, commits_nr); |
882 | 0 | ret = bitmap_writer_build(&writer); |
883 | 0 | if (ret < 0) |
884 | 0 | goto cleanup; |
885 | | |
886 | 0 | bitmap_writer_set_checksum(&writer, midx_hash); |
887 | 0 | bitmap_writer_finish(&writer, index, bitmap_name, options); |
888 | |
|
889 | 0 | cleanup: |
890 | 0 | free(index); |
891 | 0 | free(bitmap_name); |
892 | 0 | bitmap_writer_free(&writer); |
893 | |
|
894 | 0 | trace2_region_leave("midx", "write_midx_bitmap", the_repository); |
895 | |
|
896 | 0 | return ret; |
897 | 0 | } |
898 | | |
899 | | static struct multi_pack_index *lookup_multi_pack_index(struct repository *r, |
900 | | const char *object_dir) |
901 | 0 | { |
902 | 0 | struct multi_pack_index *result = NULL; |
903 | 0 | struct multi_pack_index *cur; |
904 | 0 | char *obj_dir_real = real_pathdup(object_dir, 1); |
905 | 0 | struct strbuf cur_path_real = STRBUF_INIT; |
906 | | |
907 | | /* Ensure the given object_dir is local, or a known alternate. */ |
908 | 0 | find_odb(r, obj_dir_real); |
909 | |
|
910 | 0 | for (cur = get_multi_pack_index(r); cur; cur = cur->next) { |
911 | 0 | strbuf_realpath(&cur_path_real, cur->object_dir, 1); |
912 | 0 | if (!strcmp(obj_dir_real, cur_path_real.buf)) { |
913 | 0 | result = cur; |
914 | 0 | goto cleanup; |
915 | 0 | } |
916 | 0 | } |
917 | | |
918 | 0 | cleanup: |
919 | 0 | free(obj_dir_real); |
920 | 0 | strbuf_release(&cur_path_real); |
921 | 0 | return result; |
922 | 0 | } |
923 | | |
924 | | static int fill_packs_from_midx(struct write_midx_context *ctx, |
925 | | const char *preferred_pack_name, uint32_t flags) |
926 | 0 | { |
927 | 0 | struct multi_pack_index *m; |
928 | |
|
929 | 0 | for (m = ctx->m; m; m = m->base_midx) { |
930 | 0 | uint32_t i; |
931 | |
|
932 | 0 | for (i = 0; i < m->num_packs; i++) { |
933 | 0 | ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc); |
934 | | |
935 | | /* |
936 | | * If generating a reverse index, need to have |
937 | | * packed_git's loaded to compare their |
938 | | * mtimes and object count. |
939 | | * |
940 | | * If a preferred pack is specified, need to |
941 | | * have packed_git's loaded to ensure the chosen |
942 | | * preferred pack has a non-zero object count. |
943 | | */ |
944 | 0 | if (flags & MIDX_WRITE_REV_INDEX || |
945 | 0 | preferred_pack_name) { |
946 | 0 | if (prepare_midx_pack(the_repository, m, |
947 | 0 | m->num_packs_in_base + i)) { |
948 | 0 | error(_("could not load pack")); |
949 | 0 | return 1; |
950 | 0 | } |
951 | | |
952 | 0 | if (open_pack_index(m->packs[i])) |
953 | 0 | die(_("could not open index for %s"), |
954 | 0 | m->packs[i]->pack_name); |
955 | 0 | } |
956 | | |
957 | 0 | fill_pack_info(&ctx->info[ctx->nr++], m->packs[i], |
958 | 0 | m->pack_names[i], |
959 | 0 | m->num_packs_in_base + i); |
960 | 0 | } |
961 | 0 | } |
962 | 0 | return 0; |
963 | 0 | } |
964 | | |
965 | | static struct { |
966 | | const char *non_split; |
967 | | const char *split; |
968 | | } midx_exts[] = { |
969 | | {NULL, MIDX_EXT_MIDX}, |
970 | | {MIDX_EXT_BITMAP, MIDX_EXT_BITMAP}, |
971 | | {MIDX_EXT_REV, MIDX_EXT_REV}, |
972 | | }; |
973 | | |
974 | | static int link_midx_to_chain(struct multi_pack_index *m) |
975 | 0 | { |
976 | 0 | struct strbuf from = STRBUF_INIT; |
977 | 0 | struct strbuf to = STRBUF_INIT; |
978 | 0 | int ret = 0; |
979 | 0 | size_t i; |
980 | |
|
981 | 0 | if (!m || m->has_chain) { |
982 | | /* |
983 | | * Either no MIDX previously existed, or it was already |
984 | | * part of a MIDX chain. In both cases, we have nothing |
985 | | * to link, so return early. |
986 | | */ |
987 | 0 | goto done; |
988 | 0 | } |
989 | | |
990 | 0 | for (i = 0; i < ARRAY_SIZE(midx_exts); i++) { |
991 | 0 | const unsigned char *hash = get_midx_checksum(m); |
992 | |
|
993 | 0 | get_midx_filename_ext(&from, m->object_dir, hash, |
994 | 0 | midx_exts[i].non_split); |
995 | 0 | get_split_midx_filename_ext(&to, m->object_dir, hash, |
996 | 0 | midx_exts[i].split); |
997 | |
|
998 | 0 | if (link(from.buf, to.buf) < 0 && errno != ENOENT) { |
999 | 0 | ret = error_errno(_("unable to link '%s' to '%s'"), |
1000 | 0 | from.buf, to.buf); |
1001 | 0 | goto done; |
1002 | 0 | } |
1003 | | |
1004 | 0 | strbuf_reset(&from); |
1005 | 0 | strbuf_reset(&to); |
1006 | 0 | } |
1007 | | |
1008 | 0 | done: |
1009 | 0 | strbuf_release(&from); |
1010 | 0 | strbuf_release(&to); |
1011 | 0 | return ret; |
1012 | 0 | } |
1013 | | |
1014 | | static void clear_midx_files(const char *object_dir, |
1015 | | const char **hashes, |
1016 | | uint32_t hashes_nr, |
1017 | | unsigned incremental) |
1018 | 0 | { |
1019 | | /* |
1020 | | * if incremental: |
1021 | | * - remove all non-incremental MIDX files |
1022 | | * - remove any incremental MIDX files not in the current one |
1023 | | * |
1024 | | * if non-incremental: |
1025 | | * - remove all incremental MIDX files |
1026 | | * - remove any non-incremental MIDX files not matching the current |
1027 | | * hash |
1028 | | */ |
1029 | 0 | struct strbuf buf = STRBUF_INIT; |
1030 | 0 | const char *exts[] = { MIDX_EXT_BITMAP, MIDX_EXT_REV, MIDX_EXT_MIDX }; |
1031 | 0 | uint32_t i, j; |
1032 | |
|
1033 | 0 | for (i = 0; i < ARRAY_SIZE(exts); i++) { |
1034 | 0 | clear_incremental_midx_files_ext(object_dir, exts[i], |
1035 | 0 | hashes, hashes_nr); |
1036 | 0 | for (j = 0; j < hashes_nr; j++) |
1037 | 0 | clear_midx_files_ext(object_dir, exts[i], hashes[j]); |
1038 | 0 | } |
1039 | |
|
1040 | 0 | if (incremental) |
1041 | 0 | get_midx_filename(&buf, object_dir); |
1042 | 0 | else |
1043 | 0 | get_midx_chain_filename(&buf, object_dir); |
1044 | |
|
1045 | 0 | if (unlink(buf.buf) && errno != ENOENT) |
1046 | 0 | die_errno(_("failed to clear multi-pack-index at %s"), buf.buf); |
1047 | | |
1048 | 0 | strbuf_release(&buf); |
1049 | 0 | } |
1050 | | |
1051 | | static int write_midx_internal(const char *object_dir, |
1052 | | struct string_list *packs_to_include, |
1053 | | struct string_list *packs_to_drop, |
1054 | | const char *preferred_pack_name, |
1055 | | const char *refs_snapshot, |
1056 | | unsigned flags) |
1057 | 0 | { |
1058 | 0 | struct strbuf midx_name = STRBUF_INIT; |
1059 | 0 | unsigned char midx_hash[GIT_MAX_RAWSZ]; |
1060 | 0 | uint32_t i, start_pack; |
1061 | 0 | struct hashfile *f = NULL; |
1062 | 0 | struct lock_file lk; |
1063 | 0 | struct tempfile *incr; |
1064 | 0 | struct write_midx_context ctx = { 0 }; |
1065 | 0 | int bitmapped_packs_concat_len = 0; |
1066 | 0 | int pack_name_concat_len = 0; |
1067 | 0 | int dropped_packs = 0; |
1068 | 0 | int result = 0; |
1069 | 0 | const char **keep_hashes = NULL; |
1070 | 0 | struct chunkfile *cf; |
1071 | |
|
1072 | 0 | trace2_region_enter("midx", "write_midx_internal", the_repository); |
1073 | |
|
1074 | 0 | ctx.incremental = !!(flags & MIDX_WRITE_INCREMENTAL); |
1075 | 0 | if (ctx.incremental && (flags & MIDX_WRITE_BITMAP)) |
1076 | 0 | die(_("cannot write incremental MIDX with bitmap")); |
1077 | | |
1078 | 0 | if (ctx.incremental) |
1079 | 0 | strbuf_addf(&midx_name, |
1080 | 0 | "%s/pack/multi-pack-index.d/tmp_midx_XXXXXX", |
1081 | 0 | object_dir); |
1082 | 0 | else |
1083 | 0 | get_midx_filename(&midx_name, object_dir); |
1084 | 0 | if (safe_create_leading_directories(midx_name.buf)) |
1085 | 0 | die_errno(_("unable to create leading directories of %s"), |
1086 | 0 | midx_name.buf); |
1087 | | |
1088 | 0 | if (!packs_to_include || ctx.incremental) { |
1089 | 0 | struct multi_pack_index *m = lookup_multi_pack_index(the_repository, |
1090 | 0 | object_dir); |
1091 | 0 | if (m && !midx_checksum_valid(m)) { |
1092 | 0 | warning(_("ignoring existing multi-pack-index; checksum mismatch")); |
1093 | 0 | m = NULL; |
1094 | 0 | } |
1095 | |
|
1096 | 0 | if (m) { |
1097 | | /* |
1098 | | * Only reference an existing MIDX when not filtering |
1099 | | * which packs to include, since all packs and objects |
1100 | | * are copied blindly from an existing MIDX if one is |
1101 | | * present. |
1102 | | */ |
1103 | 0 | if (ctx.incremental) |
1104 | 0 | ctx.base_midx = m; |
1105 | 0 | else if (!packs_to_include) |
1106 | 0 | ctx.m = m; |
1107 | 0 | } |
1108 | 0 | } |
1109 | |
|
1110 | 0 | ctx.nr = 0; |
1111 | 0 | ctx.alloc = ctx.m ? ctx.m->num_packs + ctx.m->num_packs_in_base : 16; |
1112 | 0 | ctx.info = NULL; |
1113 | 0 | ALLOC_ARRAY(ctx.info, ctx.alloc); |
1114 | |
|
1115 | 0 | if (ctx.incremental) { |
1116 | 0 | struct multi_pack_index *m = ctx.base_midx; |
1117 | 0 | while (m) { |
1118 | 0 | ctx.num_multi_pack_indexes_before++; |
1119 | 0 | m = m->base_midx; |
1120 | 0 | } |
1121 | 0 | } else if (ctx.m && fill_packs_from_midx(&ctx, preferred_pack_name, |
1122 | 0 | flags) < 0) { |
1123 | 0 | goto cleanup; |
1124 | 0 | } |
1125 | | |
1126 | 0 | start_pack = ctx.nr; |
1127 | |
|
1128 | 0 | ctx.pack_paths_checked = 0; |
1129 | 0 | if (flags & MIDX_PROGRESS) |
1130 | 0 | ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0); |
1131 | 0 | else |
1132 | 0 | ctx.progress = NULL; |
1133 | |
|
1134 | 0 | ctx.to_include = packs_to_include; |
1135 | |
|
1136 | 0 | for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx); |
1137 | 0 | stop_progress(&ctx.progress); |
1138 | |
|
1139 | 0 | if ((ctx.m && ctx.nr == ctx.m->num_packs + ctx.m->num_packs_in_base) && |
1140 | 0 | !ctx.incremental && |
1141 | 0 | !(packs_to_include || packs_to_drop)) { |
1142 | 0 | struct bitmap_index *bitmap_git; |
1143 | 0 | int bitmap_exists; |
1144 | 0 | int want_bitmap = flags & MIDX_WRITE_BITMAP; |
1145 | |
|
1146 | 0 | bitmap_git = prepare_midx_bitmap_git(ctx.m); |
1147 | 0 | bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git); |
1148 | 0 | free_bitmap_index(bitmap_git); |
1149 | |
|
1150 | 0 | if (bitmap_exists || !want_bitmap) { |
1151 | | /* |
1152 | | * The correct MIDX already exists, and so does a |
1153 | | * corresponding bitmap (or one wasn't requested). |
1154 | | */ |
1155 | 0 | if (!want_bitmap) |
1156 | 0 | clear_midx_files_ext(object_dir, "bitmap", NULL); |
1157 | 0 | goto cleanup; |
1158 | 0 | } |
1159 | 0 | } |
1160 | | |
1161 | 0 | if (ctx.incremental && !ctx.nr) |
1162 | 0 | goto cleanup; /* nothing to do */ |
1163 | | |
1164 | 0 | if (preferred_pack_name) { |
1165 | 0 | ctx.preferred_pack_idx = -1; |
1166 | |
|
1167 | 0 | for (i = 0; i < ctx.nr; i++) { |
1168 | 0 | if (!cmp_idx_or_pack_name(preferred_pack_name, |
1169 | 0 | ctx.info[i].pack_name)) { |
1170 | 0 | ctx.preferred_pack_idx = i; |
1171 | 0 | break; |
1172 | 0 | } |
1173 | 0 | } |
1174 | |
|
1175 | 0 | if (ctx.preferred_pack_idx == -1) |
1176 | 0 | warning(_("unknown preferred pack: '%s'"), |
1177 | 0 | preferred_pack_name); |
1178 | 0 | } else if (ctx.nr && |
1179 | 0 | (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) { |
1180 | 0 | struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p; |
1181 | 0 | ctx.preferred_pack_idx = 0; |
1182 | |
|
1183 | 0 | if (packs_to_drop && packs_to_drop->nr) |
1184 | 0 | BUG("cannot write a MIDX bitmap during expiration"); |
1185 | | |
1186 | | /* |
1187 | | * set a preferred pack when writing a bitmap to ensure that |
1188 | | * the pack from which the first object is selected in pseudo |
1189 | | * pack-order has all of its objects selected from that pack |
1190 | | * (and not another pack containing a duplicate) |
1191 | | */ |
1192 | 0 | for (i = 1; i < ctx.nr; i++) { |
1193 | 0 | struct packed_git *p = ctx.info[i].p; |
1194 | |
|
1195 | 0 | if (!oldest->num_objects || p->mtime < oldest->mtime) { |
1196 | 0 | oldest = p; |
1197 | 0 | ctx.preferred_pack_idx = i; |
1198 | 0 | } |
1199 | 0 | } |
1200 | |
|
1201 | 0 | if (!oldest->num_objects) { |
1202 | | /* |
1203 | | * If all packs are empty; unset the preferred index. |
1204 | | * This is acceptable since there will be no duplicate |
1205 | | * objects to resolve, so the preferred value doesn't |
1206 | | * matter. |
1207 | | */ |
1208 | 0 | ctx.preferred_pack_idx = -1; |
1209 | 0 | } |
1210 | 0 | } else { |
1211 | | /* |
1212 | | * otherwise don't mark any pack as preferred to avoid |
1213 | | * interfering with expiration logic below |
1214 | | */ |
1215 | 0 | ctx.preferred_pack_idx = -1; |
1216 | 0 | } |
1217 | | |
1218 | 0 | if (ctx.preferred_pack_idx > -1) { |
1219 | 0 | struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p; |
1220 | 0 | if (!preferred->num_objects) { |
1221 | 0 | error(_("cannot select preferred pack %s with no objects"), |
1222 | 0 | preferred->pack_name); |
1223 | 0 | result = 1; |
1224 | 0 | goto cleanup; |
1225 | 0 | } |
1226 | 0 | } |
1227 | | |
1228 | 0 | compute_sorted_entries(&ctx, start_pack); |
1229 | |
|
1230 | 0 | ctx.large_offsets_needed = 0; |
1231 | 0 | for (i = 0; i < ctx.entries_nr; i++) { |
1232 | 0 | if (ctx.entries[i].offset > 0x7fffffff) |
1233 | 0 | ctx.num_large_offsets++; |
1234 | 0 | if (ctx.entries[i].offset > 0xffffffff) |
1235 | 0 | ctx.large_offsets_needed = 1; |
1236 | 0 | } |
1237 | |
|
1238 | 0 | QSORT(ctx.info, ctx.nr, pack_info_compare); |
1239 | |
|
1240 | 0 | if (packs_to_drop && packs_to_drop->nr) { |
1241 | 0 | int drop_index = 0; |
1242 | 0 | int missing_drops = 0; |
1243 | |
|
1244 | 0 | for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) { |
1245 | 0 | int cmp = strcmp(ctx.info[i].pack_name, |
1246 | 0 | packs_to_drop->items[drop_index].string); |
1247 | |
|
1248 | 0 | if (!cmp) { |
1249 | 0 | drop_index++; |
1250 | 0 | ctx.info[i].expired = 1; |
1251 | 0 | } else if (cmp > 0) { |
1252 | 0 | error(_("did not see pack-file %s to drop"), |
1253 | 0 | packs_to_drop->items[drop_index].string); |
1254 | 0 | drop_index++; |
1255 | 0 | missing_drops++; |
1256 | 0 | i--; |
1257 | 0 | } else { |
1258 | 0 | ctx.info[i].expired = 0; |
1259 | 0 | } |
1260 | 0 | } |
1261 | |
|
1262 | 0 | if (missing_drops) { |
1263 | 0 | result = 1; |
1264 | 0 | goto cleanup; |
1265 | 0 | } |
1266 | 0 | } |
1267 | | |
1268 | | /* |
1269 | | * pack_perm stores a permutation between pack-int-ids from the |
1270 | | * previous multi-pack-index to the new one we are writing: |
1271 | | * |
1272 | | * pack_perm[old_id] = new_id |
1273 | | */ |
1274 | 0 | ALLOC_ARRAY(ctx.pack_perm, ctx.nr); |
1275 | 0 | for (i = 0; i < ctx.nr; i++) { |
1276 | 0 | if (ctx.info[i].expired) { |
1277 | 0 | dropped_packs++; |
1278 | 0 | ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED; |
1279 | 0 | } else { |
1280 | 0 | ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs; |
1281 | 0 | } |
1282 | 0 | } |
1283 | |
|
1284 | 0 | for (i = 0; i < ctx.nr; i++) { |
1285 | 0 | if (ctx.info[i].expired) |
1286 | 0 | continue; |
1287 | 0 | pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1; |
1288 | 0 | bitmapped_packs_concat_len += 2 * sizeof(uint32_t); |
1289 | 0 | } |
1290 | | |
1291 | | /* Check that the preferred pack wasn't expired (if given). */ |
1292 | 0 | if (preferred_pack_name) { |
1293 | 0 | struct pack_info *preferred = bsearch(preferred_pack_name, |
1294 | 0 | ctx.info, ctx.nr, |
1295 | 0 | sizeof(*ctx.info), |
1296 | 0 | idx_or_pack_name_cmp); |
1297 | 0 | if (preferred) { |
1298 | 0 | uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id]; |
1299 | 0 | if (perm == PACK_EXPIRED) |
1300 | 0 | warning(_("preferred pack '%s' is expired"), |
1301 | 0 | preferred_pack_name); |
1302 | 0 | } |
1303 | 0 | } |
1304 | |
|
1305 | 0 | if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT) |
1306 | 0 | pack_name_concat_len += MIDX_CHUNK_ALIGNMENT - |
1307 | 0 | (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT); |
1308 | |
|
1309 | 0 | if (ctx.nr - dropped_packs == 0) { |
1310 | 0 | error(_("no pack files to index.")); |
1311 | 0 | result = 1; |
1312 | 0 | goto cleanup; |
1313 | 0 | } |
1314 | | |
1315 | 0 | if (!ctx.entries_nr) { |
1316 | 0 | if (flags & MIDX_WRITE_BITMAP) |
1317 | 0 | warning(_("refusing to write multi-pack .bitmap without any objects")); |
1318 | 0 | flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP); |
1319 | 0 | } |
1320 | |
|
1321 | 0 | if (ctx.incremental) { |
1322 | 0 | struct strbuf lock_name = STRBUF_INIT; |
1323 | |
|
1324 | 0 | get_midx_chain_filename(&lock_name, object_dir); |
1325 | 0 | hold_lock_file_for_update(&lk, lock_name.buf, LOCK_DIE_ON_ERROR); |
1326 | 0 | strbuf_release(&lock_name); |
1327 | |
|
1328 | 0 | incr = mks_tempfile_m(midx_name.buf, 0444); |
1329 | 0 | if (!incr) { |
1330 | 0 | error(_("unable to create temporary MIDX layer")); |
1331 | 0 | return -1; |
1332 | 0 | } |
1333 | | |
1334 | 0 | if (adjust_shared_perm(get_tempfile_path(incr))) { |
1335 | 0 | error(_("unable to adjust shared permissions for '%s'"), |
1336 | 0 | get_tempfile_path(incr)); |
1337 | 0 | return -1; |
1338 | 0 | } |
1339 | | |
1340 | 0 | f = hashfd(get_tempfile_fd(incr), get_tempfile_path(incr)); |
1341 | 0 | } else { |
1342 | 0 | hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR); |
1343 | 0 | f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk)); |
1344 | 0 | } |
1345 | | |
1346 | 0 | cf = init_chunkfile(f); |
1347 | |
|
1348 | 0 | add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len, |
1349 | 0 | write_midx_pack_names); |
1350 | 0 | add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE, |
1351 | 0 | write_midx_oid_fanout); |
1352 | 0 | add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, |
1353 | 0 | st_mult(ctx.entries_nr, the_hash_algo->rawsz), |
1354 | 0 | write_midx_oid_lookup); |
1355 | 0 | add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, |
1356 | 0 | st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH), |
1357 | 0 | write_midx_object_offsets); |
1358 | |
|
1359 | 0 | if (ctx.large_offsets_needed) |
1360 | 0 | add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, |
1361 | 0 | st_mult(ctx.num_large_offsets, |
1362 | 0 | MIDX_CHUNK_LARGE_OFFSET_WIDTH), |
1363 | 0 | write_midx_large_offsets); |
1364 | |
|
1365 | 0 | if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) { |
1366 | 0 | ctx.pack_order = midx_pack_order(&ctx); |
1367 | 0 | add_chunk(cf, MIDX_CHUNKID_REVINDEX, |
1368 | 0 | st_mult(ctx.entries_nr, sizeof(uint32_t)), |
1369 | 0 | write_midx_revindex); |
1370 | 0 | add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS, |
1371 | 0 | bitmapped_packs_concat_len, |
1372 | 0 | write_midx_bitmapped_packs); |
1373 | 0 | } |
1374 | |
|
1375 | 0 | write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs); |
1376 | 0 | write_chunkfile(cf, &ctx); |
1377 | |
|
1378 | 0 | finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA, |
1379 | 0 | CSUM_FSYNC | CSUM_HASH_IN_STREAM); |
1380 | 0 | free_chunkfile(cf); |
1381 | |
|
1382 | 0 | if (flags & MIDX_WRITE_REV_INDEX && |
1383 | 0 | git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0)) |
1384 | 0 | write_midx_reverse_index(midx_name.buf, midx_hash, &ctx); |
1385 | |
|
1386 | 0 | if (flags & MIDX_WRITE_BITMAP) { |
1387 | 0 | struct packing_data pdata; |
1388 | 0 | struct commit **commits; |
1389 | 0 | uint32_t commits_nr; |
1390 | |
|
1391 | 0 | if (!ctx.entries_nr) |
1392 | 0 | BUG("cannot write a bitmap without any objects"); |
1393 | | |
1394 | 0 | prepare_midx_packing_data(&pdata, &ctx); |
1395 | |
|
1396 | 0 | commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx); |
1397 | | |
1398 | | /* |
1399 | | * The previous steps translated the information from |
1400 | | * 'entries' into information suitable for constructing |
1401 | | * bitmaps. We no longer need that array, so clear it to |
1402 | | * reduce memory pressure. |
1403 | | */ |
1404 | 0 | FREE_AND_NULL(ctx.entries); |
1405 | 0 | ctx.entries_nr = 0; |
1406 | |
|
1407 | 0 | if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata, |
1408 | 0 | commits, commits_nr, ctx.pack_order, |
1409 | 0 | flags) < 0) { |
1410 | 0 | error(_("could not write multi-pack bitmap")); |
1411 | 0 | result = 1; |
1412 | 0 | clear_packing_data(&pdata); |
1413 | 0 | free(commits); |
1414 | 0 | goto cleanup; |
1415 | 0 | } |
1416 | | |
1417 | 0 | clear_packing_data(&pdata); |
1418 | 0 | free(commits); |
1419 | 0 | } |
1420 | | /* |
1421 | | * NOTE: Do not use ctx.entries beyond this point, since it might |
1422 | | * have been freed in the previous if block. |
1423 | | */ |
1424 | | |
1425 | 0 | CALLOC_ARRAY(keep_hashes, ctx.num_multi_pack_indexes_before + 1); |
1426 | |
|
1427 | 0 | if (ctx.incremental) { |
1428 | 0 | FILE *chainf = fdopen_lock_file(&lk, "w"); |
1429 | 0 | struct strbuf final_midx_name = STRBUF_INIT; |
1430 | 0 | struct multi_pack_index *m = ctx.base_midx; |
1431 | |
|
1432 | 0 | if (!chainf) { |
1433 | 0 | error_errno(_("unable to open multi-pack-index chain file")); |
1434 | 0 | return -1; |
1435 | 0 | } |
1436 | | |
1437 | 0 | if (link_midx_to_chain(ctx.base_midx) < 0) |
1438 | 0 | return -1; |
1439 | | |
1440 | 0 | get_split_midx_filename_ext(&final_midx_name, object_dir, |
1441 | 0 | midx_hash, MIDX_EXT_MIDX); |
1442 | |
|
1443 | 0 | if (rename_tempfile(&incr, final_midx_name.buf) < 0) { |
1444 | 0 | error_errno(_("unable to rename new multi-pack-index layer")); |
1445 | 0 | return -1; |
1446 | 0 | } |
1447 | | |
1448 | 0 | keep_hashes[ctx.num_multi_pack_indexes_before] = |
1449 | 0 | xstrdup(hash_to_hex(midx_hash)); |
1450 | |
|
1451 | 0 | for (i = 0; i < ctx.num_multi_pack_indexes_before; i++) { |
1452 | 0 | uint32_t j = ctx.num_multi_pack_indexes_before - i - 1; |
1453 | |
|
1454 | 0 | keep_hashes[j] = xstrdup(hash_to_hex(get_midx_checksum(m))); |
1455 | 0 | m = m->base_midx; |
1456 | 0 | } |
1457 | |
|
1458 | 0 | for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++) |
1459 | 0 | fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]); |
1460 | 0 | } else { |
1461 | 0 | keep_hashes[ctx.num_multi_pack_indexes_before] = |
1462 | 0 | xstrdup(hash_to_hex(midx_hash)); |
1463 | 0 | } |
1464 | | |
1465 | 0 | if (ctx.m || ctx.base_midx) |
1466 | 0 | close_object_store(the_repository->objects); |
1467 | |
|
1468 | 0 | if (commit_lock_file(&lk) < 0) |
1469 | 0 | die_errno(_("could not write multi-pack-index")); |
1470 | | |
1471 | 0 | clear_midx_files(object_dir, keep_hashes, |
1472 | 0 | ctx.num_multi_pack_indexes_before + 1, |
1473 | 0 | ctx.incremental); |
1474 | |
|
1475 | 0 | cleanup: |
1476 | 0 | for (i = 0; i < ctx.nr; i++) { |
1477 | 0 | if (ctx.info[i].p) { |
1478 | 0 | close_pack(ctx.info[i].p); |
1479 | 0 | free(ctx.info[i].p); |
1480 | 0 | } |
1481 | 0 | free(ctx.info[i].pack_name); |
1482 | 0 | } |
1483 | |
|
1484 | 0 | free(ctx.info); |
1485 | 0 | free(ctx.entries); |
1486 | 0 | free(ctx.pack_perm); |
1487 | 0 | free(ctx.pack_order); |
1488 | 0 | if (keep_hashes) { |
1489 | 0 | for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++) |
1490 | 0 | free((char *)keep_hashes[i]); |
1491 | 0 | free(keep_hashes); |
1492 | 0 | } |
1493 | 0 | strbuf_release(&midx_name); |
1494 | |
|
1495 | 0 | trace2_region_leave("midx", "write_midx_internal", the_repository); |
1496 | |
|
1497 | 0 | return result; |
1498 | 0 | } |
1499 | | |
1500 | | int write_midx_file(const char *object_dir, |
1501 | | const char *preferred_pack_name, |
1502 | | const char *refs_snapshot, |
1503 | | unsigned flags) |
1504 | 0 | { |
1505 | 0 | return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name, |
1506 | 0 | refs_snapshot, flags); |
1507 | 0 | } |
1508 | | |
1509 | | int write_midx_file_only(const char *object_dir, |
1510 | | struct string_list *packs_to_include, |
1511 | | const char *preferred_pack_name, |
1512 | | const char *refs_snapshot, |
1513 | | unsigned flags) |
1514 | 0 | { |
1515 | 0 | return write_midx_internal(object_dir, packs_to_include, NULL, |
1516 | 0 | preferred_pack_name, refs_snapshot, flags); |
1517 | 0 | } |
1518 | | |
1519 | | int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags) |
1520 | 0 | { |
1521 | 0 | uint32_t i, *count, result = 0; |
1522 | 0 | struct string_list packs_to_drop = STRING_LIST_INIT_DUP; |
1523 | 0 | struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir); |
1524 | 0 | struct progress *progress = NULL; |
1525 | |
|
1526 | 0 | if (!m) |
1527 | 0 | return 0; |
1528 | | |
1529 | 0 | if (m->base_midx) |
1530 | 0 | die(_("cannot expire packs from an incremental multi-pack-index")); |
1531 | | |
1532 | 0 | CALLOC_ARRAY(count, m->num_packs); |
1533 | |
|
1534 | 0 | if (flags & MIDX_PROGRESS) |
1535 | 0 | progress = start_delayed_progress(_("Counting referenced objects"), |
1536 | 0 | m->num_objects); |
1537 | 0 | for (i = 0; i < m->num_objects; i++) { |
1538 | 0 | int pack_int_id = nth_midxed_pack_int_id(m, i); |
1539 | 0 | count[pack_int_id]++; |
1540 | 0 | display_progress(progress, i + 1); |
1541 | 0 | } |
1542 | 0 | stop_progress(&progress); |
1543 | |
|
1544 | 0 | if (flags & MIDX_PROGRESS) |
1545 | 0 | progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"), |
1546 | 0 | m->num_packs); |
1547 | 0 | for (i = 0; i < m->num_packs; i++) { |
1548 | 0 | char *pack_name; |
1549 | 0 | display_progress(progress, i + 1); |
1550 | |
|
1551 | 0 | if (count[i]) |
1552 | 0 | continue; |
1553 | | |
1554 | 0 | if (prepare_midx_pack(r, m, i)) |
1555 | 0 | continue; |
1556 | | |
1557 | 0 | if (m->packs[i]->pack_keep || m->packs[i]->is_cruft) |
1558 | 0 | continue; |
1559 | | |
1560 | 0 | pack_name = xstrdup(m->packs[i]->pack_name); |
1561 | 0 | close_pack(m->packs[i]); |
1562 | |
|
1563 | 0 | string_list_insert(&packs_to_drop, m->pack_names[i]); |
1564 | 0 | unlink_pack_path(pack_name, 0); |
1565 | 0 | free(pack_name); |
1566 | 0 | } |
1567 | 0 | stop_progress(&progress); |
1568 | |
|
1569 | 0 | free(count); |
1570 | |
|
1571 | 0 | if (packs_to_drop.nr) |
1572 | 0 | result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags); |
1573 | |
|
1574 | 0 | string_list_clear(&packs_to_drop, 0); |
1575 | |
|
1576 | 0 | return result; |
1577 | 0 | } |
1578 | | |
1579 | | struct repack_info { |
1580 | | timestamp_t mtime; |
1581 | | uint32_t referenced_objects; |
1582 | | uint32_t pack_int_id; |
1583 | | }; |
1584 | | |
1585 | | static int compare_by_mtime(const void *a_, const void *b_) |
1586 | 0 | { |
1587 | 0 | const struct repack_info *a, *b; |
1588 | |
|
1589 | 0 | a = (const struct repack_info *)a_; |
1590 | 0 | b = (const struct repack_info *)b_; |
1591 | |
|
1592 | 0 | if (a->mtime < b->mtime) |
1593 | 0 | return -1; |
1594 | 0 | if (a->mtime > b->mtime) |
1595 | 0 | return 1; |
1596 | 0 | return 0; |
1597 | 0 | } |
1598 | | |
1599 | | static int want_included_pack(struct repository *r, |
1600 | | struct multi_pack_index *m, |
1601 | | int pack_kept_objects, |
1602 | | uint32_t pack_int_id) |
1603 | 0 | { |
1604 | 0 | struct packed_git *p; |
1605 | 0 | if (prepare_midx_pack(r, m, pack_int_id)) |
1606 | 0 | return 0; |
1607 | 0 | p = m->packs[pack_int_id]; |
1608 | 0 | if (!pack_kept_objects && p->pack_keep) |
1609 | 0 | return 0; |
1610 | 0 | if (p->is_cruft) |
1611 | 0 | return 0; |
1612 | 0 | if (open_pack_index(p) || !p->num_objects) |
1613 | 0 | return 0; |
1614 | 0 | return 1; |
1615 | 0 | } |
1616 | | |
1617 | | static void fill_included_packs_all(struct repository *r, |
1618 | | struct multi_pack_index *m, |
1619 | | unsigned char *include_pack) |
1620 | 0 | { |
1621 | 0 | uint32_t i; |
1622 | 0 | int pack_kept_objects = 0; |
1623 | |
|
1624 | 0 | repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects); |
1625 | |
|
1626 | 0 | for (i = 0; i < m->num_packs; i++) { |
1627 | 0 | if (!want_included_pack(r, m, pack_kept_objects, i)) |
1628 | 0 | continue; |
1629 | | |
1630 | 0 | include_pack[i] = 1; |
1631 | 0 | } |
1632 | 0 | } |
1633 | | |
1634 | | static void fill_included_packs_batch(struct repository *r, |
1635 | | struct multi_pack_index *m, |
1636 | | unsigned char *include_pack, |
1637 | | size_t batch_size) |
1638 | 0 | { |
1639 | 0 | uint32_t i; |
1640 | 0 | size_t total_size; |
1641 | 0 | struct repack_info *pack_info; |
1642 | 0 | int pack_kept_objects = 0; |
1643 | |
|
1644 | 0 | CALLOC_ARRAY(pack_info, m->num_packs); |
1645 | |
|
1646 | 0 | repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects); |
1647 | |
|
1648 | 0 | for (i = 0; i < m->num_packs; i++) { |
1649 | 0 | pack_info[i].pack_int_id = i; |
1650 | |
|
1651 | 0 | if (prepare_midx_pack(r, m, i)) |
1652 | 0 | continue; |
1653 | | |
1654 | 0 | pack_info[i].mtime = m->packs[i]->mtime; |
1655 | 0 | } |
1656 | |
|
1657 | 0 | for (i = 0; i < m->num_objects; i++) { |
1658 | 0 | uint32_t pack_int_id = nth_midxed_pack_int_id(m, i); |
1659 | 0 | pack_info[pack_int_id].referenced_objects++; |
1660 | 0 | } |
1661 | |
|
1662 | 0 | QSORT(pack_info, m->num_packs, compare_by_mtime); |
1663 | |
|
1664 | 0 | total_size = 0; |
1665 | 0 | for (i = 0; total_size < batch_size && i < m->num_packs; i++) { |
1666 | 0 | int pack_int_id = pack_info[i].pack_int_id; |
1667 | 0 | struct packed_git *p = m->packs[pack_int_id]; |
1668 | 0 | size_t expected_size; |
1669 | |
|
1670 | 0 | if (!want_included_pack(r, m, pack_kept_objects, pack_int_id)) |
1671 | 0 | continue; |
1672 | | |
1673 | 0 | expected_size = st_mult(p->pack_size, |
1674 | 0 | pack_info[i].referenced_objects); |
1675 | 0 | expected_size /= p->num_objects; |
1676 | |
|
1677 | 0 | if (expected_size >= batch_size) |
1678 | 0 | continue; |
1679 | | |
1680 | 0 | total_size += expected_size; |
1681 | 0 | include_pack[pack_int_id] = 1; |
1682 | 0 | } |
1683 | |
|
1684 | 0 | free(pack_info); |
1685 | 0 | } |
1686 | | |
1687 | | int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags) |
1688 | 0 | { |
1689 | 0 | int result = 0; |
1690 | 0 | uint32_t i, packs_to_repack = 0; |
1691 | 0 | unsigned char *include_pack; |
1692 | 0 | struct child_process cmd = CHILD_PROCESS_INIT; |
1693 | 0 | FILE *cmd_in; |
1694 | 0 | struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir); |
1695 | | |
1696 | | /* |
1697 | | * When updating the default for these configuration |
1698 | | * variables in builtin/repack.c, these must be adjusted |
1699 | | * to match. |
1700 | | */ |
1701 | 0 | int delta_base_offset = 1; |
1702 | 0 | int use_delta_islands = 0; |
1703 | |
|
1704 | 0 | if (!m) |
1705 | 0 | return 0; |
1706 | 0 | if (m->base_midx) |
1707 | 0 | die(_("cannot repack an incremental multi-pack-index")); |
1708 | | |
1709 | 0 | CALLOC_ARRAY(include_pack, m->num_packs); |
1710 | |
|
1711 | 0 | if (batch_size) |
1712 | 0 | fill_included_packs_batch(r, m, include_pack, batch_size); |
1713 | 0 | else |
1714 | 0 | fill_included_packs_all(r, m, include_pack); |
1715 | |
|
1716 | 0 | for (i = 0; i < m->num_packs; i++) { |
1717 | 0 | if (include_pack[i]) |
1718 | 0 | packs_to_repack++; |
1719 | 0 | } |
1720 | 0 | if (packs_to_repack <= 1) |
1721 | 0 | goto cleanup; |
1722 | | |
1723 | 0 | repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset); |
1724 | 0 | repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands); |
1725 | |
|
1726 | 0 | strvec_push(&cmd.args, "pack-objects"); |
1727 | |
|
1728 | 0 | strvec_pushf(&cmd.args, "%s/pack/pack", object_dir); |
1729 | |
|
1730 | 0 | if (delta_base_offset) |
1731 | 0 | strvec_push(&cmd.args, "--delta-base-offset"); |
1732 | 0 | if (use_delta_islands) |
1733 | 0 | strvec_push(&cmd.args, "--delta-islands"); |
1734 | |
|
1735 | 0 | if (flags & MIDX_PROGRESS) |
1736 | 0 | strvec_push(&cmd.args, "--progress"); |
1737 | 0 | else |
1738 | 0 | strvec_push(&cmd.args, "-q"); |
1739 | |
|
1740 | 0 | cmd.git_cmd = 1; |
1741 | 0 | cmd.in = cmd.out = -1; |
1742 | |
|
1743 | 0 | if (start_command(&cmd)) { |
1744 | 0 | error(_("could not start pack-objects")); |
1745 | 0 | result = 1; |
1746 | 0 | goto cleanup; |
1747 | 0 | } |
1748 | | |
1749 | 0 | cmd_in = xfdopen(cmd.in, "w"); |
1750 | |
|
1751 | 0 | for (i = 0; i < m->num_objects; i++) { |
1752 | 0 | struct object_id oid; |
1753 | 0 | uint32_t pack_int_id = nth_midxed_pack_int_id(m, i); |
1754 | |
|
1755 | 0 | if (!include_pack[pack_int_id]) |
1756 | 0 | continue; |
1757 | | |
1758 | 0 | nth_midxed_object_oid(&oid, m, i); |
1759 | 0 | fprintf(cmd_in, "%s\n", oid_to_hex(&oid)); |
1760 | 0 | } |
1761 | 0 | fclose(cmd_in); |
1762 | |
|
1763 | 0 | if (finish_command(&cmd)) { |
1764 | 0 | error(_("could not finish pack-objects")); |
1765 | 0 | result = 1; |
1766 | 0 | goto cleanup; |
1767 | 0 | } |
1768 | | |
1769 | 0 | result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags); |
1770 | |
|
1771 | 0 | cleanup: |
1772 | 0 | free(include_pack); |
1773 | 0 | return result; |
1774 | 0 | } |