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 *source = repo->objects->sources;
63 struct multi_pack_index *m = get_multi_pack_index(source);
64 strbuf_addf(&buf, "%s.pack", base_name);
65 if (m && source->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 void existing_packs_retain_cruft(struct existing_packs *existing,
230 struct packed_git *cruft)
231 {
232 struct strbuf buf = STRBUF_INIT;
233 struct string_list_item *item;
234
235 strbuf_addstr(&buf, pack_basename(cruft));
236 strbuf_strip_suffix(&buf, ".pack");
237
238 item = string_list_lookup(&existing->cruft_packs, buf.buf);
239 if (!item)
240 BUG("could not find cruft pack '%s'", pack_basename(cruft));
241
242 existing_packs_mark_retained(item);
243 strbuf_release(&buf);
244 }
245
246 void existing_packs_mark_for_deletion(struct existing_packs *existing,
247 struct string_list *names)
248
249 {
250 const struct git_hash_algo *algop = existing->repo->hash_algo;
251 existing_packs_mark_for_deletion_1(algop, names,
252 &existing->non_kept_packs);
253 existing_packs_mark_for_deletion_1(algop, names,
254 &existing->cruft_packs);
255 }
256
257 /*
258 * Mark every pack that is referenced by the existing MIDX chain as
259 * retained, so that a subsequent call to
260 * existing_packs_mark_for_deletion() will not mark them for deletion.
261 *
262 * This is used when writing an incremental MIDX layer on top of an
263 * existing chain: retained layers continue to reference the same
264 * packs on disk, so those packs must not be unlinked even if the
265 * freshly-written pack supersedes them.
266 */
267 void existing_packs_retain_midx_packs(struct existing_packs *existing)
268 {
269 struct string_list_item *item;
270 struct strbuf buf = STRBUF_INIT;
271
272 for_each_string_list_item(item, &existing->midx_packs) {
273 struct string_list_item *found;
274
275 strbuf_reset(&buf);
276 strbuf_addstr(&buf, item->string);
277 strbuf_strip_suffix(&buf, ".pack");
278 strbuf_strip_suffix(&buf, ".idx");
279
280 found = string_list_lookup(&existing->non_kept_packs, buf.buf);
281 if (found)
282 existing_packs_mark_retained(found);
283
284 found = string_list_lookup(&existing->cruft_packs, buf.buf);
285 if (found)
286 existing_packs_mark_retained(found);
287 }
288
289 strbuf_release(&buf);
290 }
291
292 static void remove_redundant_packs_1(struct repository *repo,
293 struct string_list *packs,
294 const char *packdir,
295 bool wrote_incremental_midx)
296 {
297 struct string_list_item *item;
298 for_each_string_list_item(item, packs) {
299 if (!existing_pack_is_marked_for_deletion(item))
300 continue;
301 repack_remove_redundant_pack(repo, packdir, item->string,
302 wrote_incremental_midx);
303 }
304 }
305
306 void existing_packs_remove_redundant(struct existing_packs *existing,
307 const char *packdir,
308 bool wrote_incremental_midx)
309 {
310 remove_redundant_packs_1(existing->repo, &existing->non_kept_packs,
311 packdir, wrote_incremental_midx);
312 remove_redundant_packs_1(existing->repo, &existing->cruft_packs,
313 packdir, wrote_incremental_midx);
314 }
315
316 void existing_packs_release(struct existing_packs *existing)
317 {
318 string_list_clear(&existing->kept_packs, 0);
319 string_list_clear(&existing->non_kept_packs, 0);
320 string_list_clear(&existing->cruft_packs, 0);
321 string_list_clear(&existing->midx_packs, 0);
322 }
323
324 static struct {
325 const char *name;
326 unsigned optional:1;
327 } exts[] = {
328 {".pack"},
329 {".rev", 1},
330 {".mtimes", 1},
331 {".bitmap", 1},
332 {".promisor", 1},
333 {".idx"},
334 };
335
336 struct generated_pack {
337 struct tempfile *tempfiles[ARRAY_SIZE(exts)];
338 };
339
340 struct generated_pack *generated_pack_populate(const char *name,
341 const char *packtmp)
342 {
343 struct stat statbuf;
344 struct strbuf path = STRBUF_INIT;
345 struct generated_pack *pack = xcalloc(1, sizeof(*pack));
346 size_t i;
347
348 for (i = 0; i < ARRAY_SIZE(exts); i++) {
349 strbuf_reset(&path);
350 strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name);
351
352 if (stat(path.buf, &statbuf))
353 continue;
354
355 pack->tempfiles[i] = register_tempfile(path.buf);
356 }
357
358 strbuf_release(&path);
359 return pack;
360 }
361
362 int generated_pack_has_ext(const struct generated_pack *pack, const char *ext)
363 {
364 size_t i;
365 for (i = 0; i < ARRAY_SIZE(exts); i++) {
366 if (strcmp(exts[i].name, ext))
367 continue;
368 return !!pack->tempfiles[i];
369 }
370 BUG("unknown pack extension: '%s'", ext);
371 }
372
373 void generated_pack_install(struct generated_pack *pack, const char *name,
374 const char *packdir, const char *packtmp)
375 {
376 size_t ext;
377 for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
378 char *fname;
379
380 fname = mkpathdup("%s/pack-%s%s", packdir, name,
381 exts[ext].name);
382
383 if (pack->tempfiles[ext]) {
384 const char *fname_old = get_tempfile_path(pack->tempfiles[ext]);
385 struct stat statbuffer;
386
387 if (!stat(fname_old, &statbuffer)) {
388 statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
389 chmod(fname_old, statbuffer.st_mode);
390 }
391
392 if (rename_tempfile(&pack->tempfiles[ext], fname))
393 die_errno(_("renaming pack to '%s' failed"),
394 fname);
395 } else if (!exts[ext].optional)
396 die(_("pack-objects did not write a '%s' file for pack %s-%s"),
397 exts[ext].name, packtmp, name);
398 else if (unlink(fname) < 0 && errno != ENOENT)
399 die_errno(_("could not unlink: %s"), fname);
400
401 free(fname);
402 }
403 }