Raw
1 #include "git-compat-util.h"
2 #include "dir.h"
3 #include "midx.h"
4 #include "odb.h"
5 #include "packfile.h"
6 #include "path.h"
7 #include "repack.h"
8 #include "repository.h"
9 #include "run-command.h"
10 #include "tempfile.h"
11
12 void prepare_pack_objects(struct child_process *cmd,
13 const struct pack_objects_args *args,
14 const char *out)
15 {
16 strvec_push(&cmd->args, "pack-objects");
17 if (args->window)
18 strvec_pushf(&cmd->args, "--window=%s", args->window);
19 if (args->window_memory)
20 strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
21 if (args->depth)
22 strvec_pushf(&cmd->args, "--depth=%s", args->depth);
23 if (args->threads)
24 strvec_pushf(&cmd->args, "--threads=%s", args->threads);
25 if (args->max_pack_size)
26 strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
27 if (args->no_reuse_delta)
28 strvec_pushf(&cmd->args, "--no-reuse-delta");
29 if (args->no_reuse_object)
30 strvec_pushf(&cmd->args, "--no-reuse-object");
31 if (args->name_hash_version)
32 strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version);
33 if (args->path_walk)
34 strvec_pushf(&cmd->args, "--path-walk");
35 if (args->local)
36 strvec_push(&cmd->args, "--local");
37 if (args->quiet)
38 strvec_push(&cmd->args, "--quiet");
39 if (args->delta_base_offset)
40 strvec_push(&cmd->args, "--delta-base-offset");
41 if (!args->pack_kept_objects)
42 strvec_push(&cmd->args, "--honor-pack-keep");
43 strvec_push(&cmd->args, out);
44 cmd->git_cmd = 1;
45 cmd->out = -1;
46 }
47
48 void pack_objects_args_release(struct pack_objects_args *args)
49 {
50 free(args->window);
51 free(args->window_memory);
52 free(args->depth);
53 free(args->threads);
54 list_objects_filter_release(&args->filter_options);
55 }
56
57 void repack_remove_redundant_pack(struct repository *repo, const char *dir_name,
58 const char *base_name,
59 bool wrote_incremental_midx)
60 {
61 struct strbuf buf = STRBUF_INIT;
62 struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources);
63 struct multi_pack_index *m = get_multi_pack_index(files->packed);
64 strbuf_addf(&buf, "%s.pack", base_name);
65 if (m && files->base.local && midx_contains_pack(m, buf.buf)) {
66 clear_midx_file(repo);
67 if (!wrote_incremental_midx)
68 clear_incremental_midx_files(repo, NULL);
69 }
70 strbuf_insertf(&buf, 0, "%s/", dir_name);
71 unlink_pack_path(buf.buf, 1);
72 strbuf_release(&buf);
73 }
74
75 const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts)
76 {
77 const char *pack_prefix;
78 if (!skip_prefix(opts->packtmp, opts->packdir, &pack_prefix))
79 die(_("pack prefix %s does not begin with objdir %s"),
80 opts->packtmp, opts->packdir);
81 if (*pack_prefix == '/')
82 pack_prefix++;
83 return pack_prefix;
84 }
85
86 bool write_pack_opts_is_local(const struct write_pack_opts *opts)
87 {
88 return starts_with(opts->destination, opts->packdir);
89 }
90
91 int finish_pack_objects_cmd(const struct git_hash_algo *algop,
92 const struct write_pack_opts *opts,
93 struct child_process *cmd,
94 struct string_list *names)
95 {
96 FILE *out;
97 bool local = write_pack_opts_is_local(opts);
98 struct strbuf line = STRBUF_INIT;
99
100 out = xfdopen(cmd->out, "r");
101 while (strbuf_getline_lf(&line, out) != EOF) {
102 struct string_list_item *item;
103
104 if (line.len != algop->hexsz)
105 die(_("repack: Expecting full hex object ID lines only "
106 "from pack-objects."));
107 /*
108 * Avoid putting packs written outside of the repository in the
109 * list of names.
110 */
111 if (local) {
112 item = string_list_append(names, line.buf);
113 item->util = generated_pack_populate(line.buf,
114 opts->packtmp);
115 }
116 }
117 fclose(out);
118
119 strbuf_release(&line);
120
121 return finish_command(cmd);
122 }
123
124 #define DELETE_PACK 1
125 #define RETAIN_PACK 2
126
127 void existing_packs_collect(struct existing_packs *existing,
128 const struct string_list *extra_keep)
129 {
130 struct packed_git *p;
131 struct strbuf buf = STRBUF_INIT;
132
133 repo_for_each_pack(existing->repo, p) {
134 size_t i;
135 const char *base;
136
137 if (p->multi_pack_index)
138 string_list_append(&existing->midx_packs,
139 pack_basename(p));
140 if (!p->pack_local)
141 continue;
142
143 base = pack_basename(p);
144
145 for (i = 0; i < extra_keep->nr; i++)
146 if (!fspathcmp(base, extra_keep->items[i].string))
147 break;
148
149 strbuf_reset(&buf);
150 strbuf_addstr(&buf, base);
151 strbuf_strip_suffix(&buf, ".pack");
152
153 if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
154 string_list_append(&existing->kept_packs, buf.buf);
155 else if (p->is_cruft)
156 string_list_append(&existing->cruft_packs, buf.buf);
157 else
158 string_list_append(&existing->non_kept_packs, buf.buf);
159 }
160
161 existing->source = existing->repo->objects->sources;
162
163 string_list_sort(&existing->kept_packs);
164 string_list_sort(&existing->non_kept_packs);
165 string_list_sort(&existing->cruft_packs);
166 string_list_sort(&existing->midx_packs);
167 strbuf_release(&buf);
168 }
169
170 int existing_packs_has_non_kept(const struct existing_packs *existing)
171 {
172 return existing->non_kept_packs.nr || existing->cruft_packs.nr;
173 }
174
175 static void existing_pack_mark_for_deletion(struct string_list_item *item)
176 {
177 item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
178 }
179
180 static void existing_pack_unmark_for_deletion(struct string_list_item *item)
181 {
182 item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
183 }
184
185 int existing_pack_is_marked_for_deletion(struct string_list_item *item)
186 {
187 return (uintptr_t)item->util & DELETE_PACK;
188 }
189
190 static void existing_packs_mark_retained(struct string_list_item *item)
191 {
192 item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
193 }
194
195 static int existing_pack_is_retained(struct string_list_item *item)
196 {
197 return (uintptr_t)item->util & RETAIN_PACK;
198 }
199
200 static void existing_packs_mark_for_deletion_1(const struct git_hash_algo *algop,
201 struct string_list *names,
202 struct string_list *list)
203 {
204 struct string_list_item *item;
205 const size_t hexsz = algop->hexsz;
206
207 for_each_string_list_item(item, list) {
208 char *sha1;
209 size_t len = strlen(item->string);
210 if (len < hexsz)
211 continue;
212 sha1 = item->string + len - hexsz;
213
214 if (existing_pack_is_retained(item)) {
215 existing_pack_unmark_for_deletion(item);
216 } else if (!string_list_has_string(names, sha1)) {
217 /*
218 * Mark this pack for deletion, which ensures
219 * that this pack won't be included in a MIDX
220 * (if `--write-midx` was given) and that we
221 * will actually delete this pack (if `-d` was
222 * given).
223 */
224 existing_pack_mark_for_deletion(item);
225 }
226 }
227 }
228
229 static struct string_list_item *locate_existing_pack(struct string_list *list,
230 struct packed_git *p)
231 {
232 struct strbuf buf = STRBUF_INIT;
233 struct string_list_item *item;
234
235 strbuf_addstr(&buf, pack_basename(p));
236 strbuf_strip_suffix(&buf, ".pack");
237
238 item = string_list_lookup(list, buf.buf);
239
240 strbuf_release(&buf);
241
242 return item;
243 }
244
245 void existing_packs_retain_all_cruft(struct existing_packs *existing)
246 {
247 struct string_list_item *item;
248
249 for_each_string_list_item(item, &existing->cruft_packs)
250 existing_packs_mark_retained(item);
251 }
252
253 void existing_packs_retain_cruft(struct existing_packs *existing,
254 struct packed_git *cruft)
255 {
256 struct string_list_item *item;
257
258 item = locate_existing_pack(&existing->cruft_packs, cruft);
259 if (!item)
260 BUG("could not find cruft pack '%s'", pack_basename(cruft));
261
262 existing_packs_mark_retained(item);
263 }
264
265 static void existing_packs_retain_non_kept(struct existing_packs *existing,
266 struct packed_git *p)
267 {
268 struct string_list_item *item;
269
270 if (!p->pack_local)
271 return;
272
273 item = locate_existing_pack(&existing->non_kept_packs, p);
274 if (!item)
275 BUG("could not find non-kept pack '%s'", pack_basename(p));
276
277 existing_packs_mark_retained(item);
278 }
279
280 void existing_packs_retain_from_geometry(struct existing_packs *existing,
281 const struct pack_geometry *geometry)
282 {
283 uint32_t i;
284
285 for (i = geometry->split; i < geometry->pack_nr; i++)
286 existing_packs_retain_non_kept(existing, geometry->pack[i]);
287 for (i = geometry->promisor_split; i < geometry->promisor_pack_nr; i++)
288 existing_packs_retain_non_kept(existing,
289 geometry->promisor_pack[i]);
290 }
291
292 void existing_packs_mark_for_deletion(struct existing_packs *existing,
293 struct string_list *names)
294
295 {
296 const struct git_hash_algo *algop = existing->repo->hash_algo;
297 existing_packs_mark_for_deletion_1(algop, names,
298 &existing->non_kept_packs);
299 existing_packs_mark_for_deletion_1(algop, names,
300 &existing->cruft_packs);
301 }
302
303 static int pack_geometry_contains_pack(struct packed_git **packs,
304 uint32_t packs_nr,
305 const char *base)
306 {
307 struct strbuf buf = STRBUF_INIT;
308 uint32_t i;
309
310 for (i = 0; i < packs_nr; i++) {
311 strbuf_reset(&buf);
312 strbuf_addstr(&buf, pack_basename(packs[i]));
313 strbuf_strip_suffix(&buf, ".pack");
314
315 if (!strcmp(buf.buf, base)) {
316 strbuf_release(&buf);
317 return 1;
318 }
319 }
320
321 strbuf_release(&buf);
322 return 0;
323 }
324
325 static int pack_geometry_contains_rollup(const struct pack_geometry *geometry,
326 const char *base)
327 {
328 if (!geometry || !geometry->split_factor)
329 return 0;
330
331 return pack_geometry_contains_pack(geometry->pack, geometry->split, base) ||
332 pack_geometry_contains_pack(geometry->promisor_pack,
333 geometry->promisor_split, base);
334 }
335
336 /*
337 * Mark every pack that is referenced by the existing MIDX chain as
338 * retained, so that a subsequent call to
339 * existing_packs_mark_for_deletion() will not mark them for deletion.
340 *
341 * This is used when writing an incremental MIDX layer on top of an
342 * existing chain: retained layers continue to reference the same
343 * packs on disk, so those packs must not be unlinked even if the
344 * freshly-written pack supersedes them. When doing a geometric repack,
345 * packs below the split are rewritten into the new MIDX tip and should
346 * remain eligible for deletion.
347 */
348 void existing_packs_retain_midx_packs(struct existing_packs *existing,
349 const struct pack_geometry *geometry)
350 {
351 struct string_list_item *item;
352 struct strbuf buf = STRBUF_INIT;
353
354 for_each_string_list_item(item, &existing->midx_packs) {
355 struct string_list_item *found;
356
357 strbuf_reset(&buf);
358 strbuf_addstr(&buf, item->string);
359 strbuf_strip_suffix(&buf, ".pack");
360 strbuf_strip_suffix(&buf, ".idx");
361
362 if (pack_geometry_contains_rollup(geometry, buf.buf))
363 continue;
364
365 found = string_list_lookup(&existing->non_kept_packs, buf.buf);
366 if (found)
367 existing_packs_mark_retained(found);
368
369 found = string_list_lookup(&existing->cruft_packs, buf.buf);
370 if (found)
371 existing_packs_mark_retained(found);
372 }
373
374 strbuf_release(&buf);
375 }
376
377 static void remove_redundant_packs_1(struct repository *repo,
378 struct string_list *packs,
379 const char *packdir,
380 bool wrote_incremental_midx)
381 {
382 struct string_list_item *item;
383 for_each_string_list_item(item, packs) {
384 if (!existing_pack_is_marked_for_deletion(item))
385 continue;
386 repack_remove_redundant_pack(repo, packdir, item->string,
387 wrote_incremental_midx);
388 }
389 }
390
391 void existing_packs_remove_redundant(struct existing_packs *existing,
392 const char *packdir,
393 bool wrote_incremental_midx)
394 {
395 remove_redundant_packs_1(existing->repo, &existing->non_kept_packs,
396 packdir, wrote_incremental_midx);
397 remove_redundant_packs_1(existing->repo, &existing->cruft_packs,
398 packdir, wrote_incremental_midx);
399 }
400
401 void existing_packs_release(struct existing_packs *existing)
402 {
403 string_list_clear(&existing->kept_packs, 0);
404 string_list_clear(&existing->non_kept_packs, 0);
405 string_list_clear(&existing->cruft_packs, 0);
406 string_list_clear(&existing->midx_packs, 0);
407 }
408
409 static struct {
410 const char *name;
411 unsigned optional:1;
412 } exts[] = {
413 {".pack"},
414 {".rev", 1},
415 {".mtimes", 1},
416 {".bitmap", 1},
417 {".promisor", 1},
418 {".idx"},
419 };
420
421 struct generated_pack {
422 struct tempfile *tempfiles[ARRAY_SIZE(exts)];
423 };
424
425 struct generated_pack *generated_pack_populate(const char *name,
426 const char *packtmp)
427 {
428 struct stat statbuf;
429 struct strbuf path = STRBUF_INIT;
430 struct generated_pack *pack = xcalloc(1, sizeof(*pack));
431 size_t i;
432
433 for (i = 0; i < ARRAY_SIZE(exts); i++) {
434 strbuf_reset(&path);
435 strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name);
436
437 if (stat(path.buf, &statbuf))
438 continue;
439
440 pack->tempfiles[i] = register_tempfile(path.buf);
441 }
442
443 strbuf_release(&path);
444 return pack;
445 }
446
447 int generated_pack_has_ext(const struct generated_pack *pack, const char *ext)
448 {
449 size_t i;
450 for (i = 0; i < ARRAY_SIZE(exts); i++) {
451 if (strcmp(exts[i].name, ext))
452 continue;
453 return !!pack->tempfiles[i];
454 }
455 BUG("unknown pack extension: '%s'", ext);
456 }
457
458 void generated_pack_install(struct generated_pack *pack, const char *name,
459 const char *packdir, const char *packtmp)
460 {
461 size_t ext;
462 for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
463 char *fname;
464
465 fname = mkpathdup("%s/pack-%s%s", packdir, name,
466 exts[ext].name);
467
468 if (pack->tempfiles[ext]) {
469 const char *fname_old = get_tempfile_path(pack->tempfiles[ext]);
470 struct stat statbuffer;
471
472 if (!stat(fname_old, &statbuffer)) {
473 statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
474 chmod(fname_old, statbuffer.st_mode);
475 }
476
477 if (rename_tempfile(&pack->tempfiles[ext], fname))
478 die_errno(_("renaming pack to '%s' failed"),
479 fname);
480 } else if (!exts[ext].optional)
481 die(_("pack-objects did not write a '%s' file for pack %s-%s"),
482 exts[ext].name, packtmp, name);
483 else if (unlink(fname) < 0 && errno != ENOENT)
484 die_errno(_("could not unlink: %s"), fname);
485
486 free(fname);
487 }
488 }