Line | Count | Source |
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 | 0 | { |
16 | 0 | strvec_push(&cmd->args, "pack-objects"); |
17 | 0 | if (args->window) |
18 | 0 | strvec_pushf(&cmd->args, "--window=%s", args->window); |
19 | 0 | if (args->window_memory) |
20 | 0 | strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory); |
21 | 0 | if (args->depth) |
22 | 0 | strvec_pushf(&cmd->args, "--depth=%s", args->depth); |
23 | 0 | if (args->threads) |
24 | 0 | strvec_pushf(&cmd->args, "--threads=%s", args->threads); |
25 | 0 | if (args->max_pack_size) |
26 | 0 | strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size); |
27 | 0 | if (args->no_reuse_delta) |
28 | 0 | strvec_pushf(&cmd->args, "--no-reuse-delta"); |
29 | 0 | if (args->no_reuse_object) |
30 | 0 | strvec_pushf(&cmd->args, "--no-reuse-object"); |
31 | 0 | if (args->name_hash_version) |
32 | 0 | strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version); |
33 | 0 | if (args->path_walk) |
34 | 0 | strvec_pushf(&cmd->args, "--path-walk"); |
35 | 0 | if (args->local) |
36 | 0 | strvec_push(&cmd->args, "--local"); |
37 | 0 | if (args->quiet) |
38 | 0 | strvec_push(&cmd->args, "--quiet"); |
39 | 0 | if (args->delta_base_offset) |
40 | 0 | strvec_push(&cmd->args, "--delta-base-offset"); |
41 | 0 | if (!args->pack_kept_objects) |
42 | 0 | strvec_push(&cmd->args, "--honor-pack-keep"); |
43 | 0 | strvec_push(&cmd->args, out); |
44 | 0 | cmd->git_cmd = 1; |
45 | 0 | cmd->out = -1; |
46 | 0 | } |
47 | | |
48 | | void pack_objects_args_release(struct pack_objects_args *args) |
49 | 0 | { |
50 | 0 | free(args->window); |
51 | 0 | free(args->window_memory); |
52 | 0 | free(args->depth); |
53 | 0 | free(args->threads); |
54 | 0 | list_objects_filter_release(&args->filter_options); |
55 | 0 | } |
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 | 0 | { |
61 | 0 | struct strbuf buf = STRBUF_INIT; |
62 | 0 | struct odb_source_files *files = odb_source_files_downcast(repo->objects->source); |
63 | 0 | struct multi_pack_index *m = get_multi_pack_index(files->dirs->packed); |
64 | 0 | strbuf_addf(&buf, "%s.pack", base_name); |
65 | 0 | if (m && files->dirs->local && midx_contains_pack(m, buf.buf)) { |
66 | 0 | clear_midx_file(repo); |
67 | 0 | if (!wrote_incremental_midx) |
68 | 0 | clear_incremental_midx_files(repo, NULL); |
69 | 0 | } |
70 | 0 | strbuf_insertf(&buf, 0, "%s/", dir_name); |
71 | 0 | unlink_pack_path(buf.buf, 1); |
72 | 0 | strbuf_release(&buf); |
73 | 0 | } |
74 | | |
75 | | const char *write_pack_opts_pack_prefix(const struct write_pack_opts *opts) |
76 | 0 | { |
77 | 0 | const char *pack_prefix; |
78 | 0 | if (!skip_prefix(opts->packtmp, opts->packdir, &pack_prefix)) |
79 | 0 | die(_("pack prefix %s does not begin with objdir %s"), |
80 | 0 | opts->packtmp, opts->packdir); |
81 | 0 | if (*pack_prefix == '/') |
82 | 0 | pack_prefix++; |
83 | 0 | return pack_prefix; |
84 | 0 | } |
85 | | |
86 | | bool write_pack_opts_is_local(const struct write_pack_opts *opts) |
87 | 0 | { |
88 | 0 | return starts_with(opts->destination, opts->packdir); |
89 | 0 | } |
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 | 0 | { |
96 | 0 | FILE *out; |
97 | 0 | bool local = write_pack_opts_is_local(opts); |
98 | 0 | struct strbuf line = STRBUF_INIT; |
99 | |
|
100 | 0 | out = xfdopen(cmd->out, "r"); |
101 | 0 | while (strbuf_getline_lf(&line, out) != EOF) { |
102 | 0 | struct string_list_item *item; |
103 | |
|
104 | 0 | if (line.len != algop->hexsz) |
105 | 0 | die(_("repack: Expecting full hex object ID lines only " |
106 | 0 | "from pack-objects.")); |
107 | | /* |
108 | | * Avoid putting packs written outside of the repository in the |
109 | | * list of names. |
110 | | */ |
111 | 0 | if (local) { |
112 | 0 | item = string_list_append(names, line.buf); |
113 | 0 | item->util = generated_pack_populate(line.buf, |
114 | 0 | opts->packtmp); |
115 | 0 | } |
116 | 0 | } |
117 | 0 | fclose(out); |
118 | |
|
119 | 0 | strbuf_release(&line); |
120 | |
|
121 | 0 | return finish_command(cmd); |
122 | 0 | } |
123 | | |
124 | 0 | #define DELETE_PACK 1 |
125 | 0 | #define RETAIN_PACK 2 |
126 | | |
127 | | void existing_packs_collect(struct existing_packs *existing, |
128 | | const struct string_list *extra_keep) |
129 | 0 | { |
130 | 0 | struct packed_git *p; |
131 | 0 | struct strbuf buf = STRBUF_INIT; |
132 | |
|
133 | 0 | repo_for_each_pack(existing->repo, p) { |
134 | 0 | size_t i; |
135 | 0 | const char *base; |
136 | |
|
137 | 0 | if (p->multi_pack_index) |
138 | 0 | string_list_append(&existing->midx_packs, |
139 | 0 | pack_basename(p)); |
140 | 0 | if (!p->pack_local) |
141 | 0 | continue; |
142 | | |
143 | 0 | base = pack_basename(p); |
144 | |
|
145 | 0 | for (i = 0; i < extra_keep->nr; i++) |
146 | 0 | if (!fspathcmp(base, extra_keep->items[i].string)) |
147 | 0 | break; |
148 | |
|
149 | 0 | strbuf_reset(&buf); |
150 | 0 | strbuf_addstr(&buf, base); |
151 | 0 | strbuf_strip_suffix(&buf, ".pack"); |
152 | |
|
153 | 0 | if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) |
154 | 0 | string_list_append(&existing->kept_packs, buf.buf); |
155 | 0 | else if (p->is_cruft) |
156 | 0 | string_list_append(&existing->cruft_packs, buf.buf); |
157 | 0 | else |
158 | 0 | string_list_append(&existing->non_kept_packs, buf.buf); |
159 | 0 | } |
160 | |
|
161 | 0 | existing->source = existing->repo->objects->source; |
162 | |
|
163 | 0 | string_list_sort(&existing->kept_packs); |
164 | 0 | string_list_sort(&existing->non_kept_packs); |
165 | 0 | string_list_sort(&existing->cruft_packs); |
166 | 0 | string_list_sort(&existing->midx_packs); |
167 | 0 | strbuf_release(&buf); |
168 | 0 | } |
169 | | |
170 | | int existing_packs_has_non_kept(const struct existing_packs *existing) |
171 | 0 | { |
172 | 0 | return existing->non_kept_packs.nr || existing->cruft_packs.nr; |
173 | 0 | } |
174 | | |
175 | | static void existing_pack_mark_for_deletion(struct string_list_item *item) |
176 | 0 | { |
177 | 0 | item->util = (void*)((uintptr_t)item->util | DELETE_PACK); |
178 | 0 | } |
179 | | |
180 | | static void existing_pack_unmark_for_deletion(struct string_list_item *item) |
181 | 0 | { |
182 | 0 | item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK); |
183 | 0 | } |
184 | | |
185 | | int existing_pack_is_marked_for_deletion(struct string_list_item *item) |
186 | 0 | { |
187 | 0 | return (uintptr_t)item->util & DELETE_PACK; |
188 | 0 | } |
189 | | |
190 | | static void existing_packs_mark_retained(struct string_list_item *item) |
191 | 0 | { |
192 | 0 | item->util = (void*)((uintptr_t)item->util | RETAIN_PACK); |
193 | 0 | } |
194 | | |
195 | | static int existing_pack_is_retained(struct string_list_item *item) |
196 | 0 | { |
197 | 0 | return (uintptr_t)item->util & RETAIN_PACK; |
198 | 0 | } |
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 | 0 | { |
204 | 0 | struct string_list_item *item; |
205 | 0 | const size_t hexsz = algop->hexsz; |
206 | |
|
207 | 0 | for_each_string_list_item(item, list) { |
208 | 0 | char *sha1; |
209 | 0 | size_t len = strlen(item->string); |
210 | 0 | if (len < hexsz) |
211 | 0 | continue; |
212 | 0 | sha1 = item->string + len - hexsz; |
213 | |
|
214 | 0 | if (existing_pack_is_retained(item)) { |
215 | 0 | existing_pack_unmark_for_deletion(item); |
216 | 0 | } 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 | 0 | existing_pack_mark_for_deletion(item); |
225 | 0 | } |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | void existing_packs_retain_cruft(struct existing_packs *existing, |
230 | | struct packed_git *cruft) |
231 | 0 | { |
232 | 0 | struct strbuf buf = STRBUF_INIT; |
233 | 0 | struct string_list_item *item; |
234 | |
|
235 | 0 | strbuf_addstr(&buf, pack_basename(cruft)); |
236 | 0 | strbuf_strip_suffix(&buf, ".pack"); |
237 | |
|
238 | 0 | item = string_list_lookup(&existing->cruft_packs, buf.buf); |
239 | 0 | if (!item) |
240 | 0 | BUG("could not find cruft pack '%s'", pack_basename(cruft)); |
241 | | |
242 | 0 | existing_packs_mark_retained(item); |
243 | 0 | strbuf_release(&buf); |
244 | 0 | } |
245 | | |
246 | | void existing_packs_mark_for_deletion(struct existing_packs *existing, |
247 | | struct string_list *names) |
248 | | |
249 | 0 | { |
250 | 0 | const struct git_hash_algo *algop = existing->repo->hash_algo; |
251 | 0 | existing_packs_mark_for_deletion_1(algop, names, |
252 | 0 | &existing->non_kept_packs); |
253 | 0 | existing_packs_mark_for_deletion_1(algop, names, |
254 | 0 | &existing->cruft_packs); |
255 | 0 | } |
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 | 0 | { |
269 | 0 | struct string_list_item *item; |
270 | 0 | struct strbuf buf = STRBUF_INIT; |
271 | |
|
272 | 0 | for_each_string_list_item(item, &existing->midx_packs) { |
273 | 0 | struct string_list_item *found; |
274 | |
|
275 | 0 | strbuf_reset(&buf); |
276 | 0 | strbuf_addstr(&buf, item->string); |
277 | 0 | strbuf_strip_suffix(&buf, ".pack"); |
278 | 0 | strbuf_strip_suffix(&buf, ".idx"); |
279 | |
|
280 | 0 | found = string_list_lookup(&existing->non_kept_packs, buf.buf); |
281 | 0 | if (found) |
282 | 0 | existing_packs_mark_retained(found); |
283 | |
|
284 | 0 | found = string_list_lookup(&existing->cruft_packs, buf.buf); |
285 | 0 | if (found) |
286 | 0 | existing_packs_mark_retained(found); |
287 | 0 | } |
288 | |
|
289 | 0 | strbuf_release(&buf); |
290 | 0 | } |
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 | 0 | { |
297 | 0 | struct string_list_item *item; |
298 | 0 | for_each_string_list_item(item, packs) { |
299 | 0 | if (!existing_pack_is_marked_for_deletion(item)) |
300 | 0 | continue; |
301 | 0 | repack_remove_redundant_pack(repo, packdir, item->string, |
302 | 0 | wrote_incremental_midx); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | void existing_packs_remove_redundant(struct existing_packs *existing, |
307 | | const char *packdir, |
308 | | bool wrote_incremental_midx) |
309 | 0 | { |
310 | 0 | remove_redundant_packs_1(existing->repo, &existing->non_kept_packs, |
311 | 0 | packdir, wrote_incremental_midx); |
312 | 0 | remove_redundant_packs_1(existing->repo, &existing->cruft_packs, |
313 | 0 | packdir, wrote_incremental_midx); |
314 | 0 | } |
315 | | |
316 | | void existing_packs_release(struct existing_packs *existing) |
317 | 0 | { |
318 | 0 | string_list_clear(&existing->kept_packs, 0); |
319 | 0 | string_list_clear(&existing->non_kept_packs, 0); |
320 | 0 | string_list_clear(&existing->cruft_packs, 0); |
321 | 0 | string_list_clear(&existing->midx_packs, 0); |
322 | 0 | } |
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 | 0 | { |
343 | 0 | struct stat statbuf; |
344 | 0 | struct strbuf path = STRBUF_INIT; |
345 | 0 | struct generated_pack *pack = xcalloc(1, sizeof(*pack)); |
346 | 0 | size_t i; |
347 | |
|
348 | 0 | for (i = 0; i < ARRAY_SIZE(exts); i++) { |
349 | 0 | strbuf_reset(&path); |
350 | 0 | strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name); |
351 | |
|
352 | 0 | if (stat(path.buf, &statbuf)) |
353 | 0 | continue; |
354 | | |
355 | 0 | pack->tempfiles[i] = register_tempfile(path.buf); |
356 | 0 | } |
357 | |
|
358 | 0 | strbuf_release(&path); |
359 | 0 | return pack; |
360 | 0 | } |
361 | | |
362 | | int generated_pack_has_ext(const struct generated_pack *pack, const char *ext) |
363 | 0 | { |
364 | 0 | size_t i; |
365 | 0 | for (i = 0; i < ARRAY_SIZE(exts); i++) { |
366 | 0 | if (strcmp(exts[i].name, ext)) |
367 | 0 | continue; |
368 | 0 | return !!pack->tempfiles[i]; |
369 | 0 | } |
370 | 0 | BUG("unknown pack extension: '%s'", ext); |
371 | 0 | } |
372 | | |
373 | | void generated_pack_install(struct generated_pack *pack, const char *name, |
374 | | const char *packdir, const char *packtmp) |
375 | 0 | { |
376 | 0 | size_t ext; |
377 | 0 | for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { |
378 | 0 | char *fname; |
379 | |
|
380 | 0 | fname = mkpathdup("%s/pack-%s%s", packdir, name, |
381 | 0 | exts[ext].name); |
382 | |
|
383 | 0 | if (pack->tempfiles[ext]) { |
384 | 0 | const char *fname_old = get_tempfile_path(pack->tempfiles[ext]); |
385 | 0 | struct stat statbuffer; |
386 | |
|
387 | 0 | if (!stat(fname_old, &statbuffer)) { |
388 | 0 | statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); |
389 | 0 | chmod(fname_old, statbuffer.st_mode); |
390 | 0 | } |
391 | |
|
392 | 0 | if (rename_tempfile(&pack->tempfiles[ext], fname)) |
393 | 0 | die_errno(_("renaming pack to '%s' failed"), |
394 | 0 | fname); |
395 | 0 | } else if (!exts[ext].optional) |
396 | 0 | die(_("pack-objects did not write a '%s' file for pack %s-%s"), |
397 | 0 | exts[ext].name, packtmp, name); |
398 | 0 | else if (unlink(fname) < 0 && errno != ENOENT) |
399 | 0 | die_errno(_("could not unlink: %s"), fname); |
400 | | |
401 | 0 | free(fname); |
402 | 0 | } |
403 | 0 | } |