/src/git/odb/source-files.c
Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | #include "abspath.h" |
3 | | #include "blob.h" |
4 | | #include "chdir-notify.h" |
5 | | #include "config.h" |
6 | | #include "gettext.h" |
7 | | #include "hex.h" |
8 | | #include "lockfile.h" |
9 | | #include "object-file.h" |
10 | | #include "odb.h" |
11 | | #include "odb/source.h" |
12 | | #include "odb/source-files.h" |
13 | | #include "odb/source-loose.h" |
14 | | #include "pack-objects.h" |
15 | | #include "packfile.h" |
16 | | #include "path.h" |
17 | | #include "promisor-remote.h" |
18 | | #include "repack.h" |
19 | | #include "run-command.h" |
20 | | #include "strbuf.h" |
21 | | #include "string-list.h" |
22 | | #include "strmap.h" |
23 | | #include "strvec.h" |
24 | | #include "tree.h" |
25 | | #include "write-or-die.h" |
26 | | |
27 | | static void odb_source_files_reparent(const char *old_cwd, |
28 | | const char *new_cwd, |
29 | | void *cb_data) |
30 | 0 | { |
31 | 0 | struct odb_source_files *files = cb_data; |
32 | 0 | char *path = reparent_relative_path(old_cwd, new_cwd, |
33 | 0 | files->base.path); |
34 | 0 | free(files->base.path); |
35 | 0 | files->base.path = path; |
36 | 0 | } |
37 | | |
38 | | static void odb_source_files_free(struct odb_source *source) |
39 | 0 | { |
40 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
41 | 0 | chdir_notify_unregister(odb_source_files_reparent, files); |
42 | 0 | odb_source_free(&files->loose->base); |
43 | 0 | odb_source_free(&files->packed->base); |
44 | 0 | odb_source_release(&files->base); |
45 | 0 | free(files); |
46 | 0 | } |
47 | | |
48 | | static void odb_source_files_close(struct odb_source *source) |
49 | 0 | { |
50 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
51 | 0 | odb_source_close(&files->loose->base); |
52 | 0 | odb_source_close(&files->packed->base); |
53 | 0 | } |
54 | | |
55 | | static int odb_source_files_create_on_disk(struct odb_source *source, |
56 | | const struct odb_create_on_disk_options *opts) |
57 | 0 | { |
58 | 0 | struct lock_file alternates_lock = LOCK_INIT; |
59 | 0 | struct strbuf path = STRBUF_INIT; |
60 | 0 | struct strset seen = STRSET_INIT; |
61 | 0 | struct strbuf line = STRBUF_INIT; |
62 | 0 | int ret; |
63 | |
|
64 | 0 | safe_create_dir(source->odb->repo, source->path, 1); |
65 | |
|
66 | 0 | strbuf_addf(&path, "%s/pack", source->path); |
67 | 0 | safe_create_dir(source->odb->repo, path.buf, 1); |
68 | |
|
69 | 0 | strbuf_reset(&path); |
70 | 0 | strbuf_addf(&path, "%s/info", source->path); |
71 | 0 | safe_create_dir(source->odb->repo, path.buf, 1); |
72 | |
|
73 | 0 | if (opts->alternates && opts->alternates->nr) { |
74 | 0 | FILE *alternates, *orig; |
75 | |
|
76 | 0 | strbuf_reset(&path); |
77 | 0 | strbuf_addf(&path, "%s/info/alternates", source->path); |
78 | |
|
79 | 0 | repo_hold_lock_file_for_update(source->odb->repo, &alternates_lock, |
80 | 0 | path.buf, LOCK_DIE_ON_ERROR); |
81 | |
|
82 | 0 | alternates = fdopen_lock_file(&alternates_lock, "w"); |
83 | 0 | if (!alternates) { |
84 | 0 | ret = error_errno(_("unable to fdopen alternates lockfile")); |
85 | 0 | goto out; |
86 | 0 | } |
87 | | |
88 | | /* |
89 | | * The alternates file may already exist, e.g. when it has been |
90 | | * seeded from a template directory. Read any preexisting |
91 | | * entries so that we don't end up writing duplicates. |
92 | | */ |
93 | 0 | orig = fopen(path.buf, "r"); |
94 | 0 | if (orig) { |
95 | 0 | while (strbuf_getline(&line, orig) != EOF) { |
96 | 0 | strset_add(&seen, line.buf); |
97 | 0 | fprintf(alternates, "%s\n", line.buf); |
98 | 0 | } |
99 | |
|
100 | 0 | if (ferror(orig)) { |
101 | 0 | ret = error_errno(_("unable to read alternates file")); |
102 | 0 | fclose(orig); |
103 | 0 | goto out; |
104 | 0 | } |
105 | | |
106 | 0 | fclose(orig); |
107 | 0 | } else if (errno != ENOENT) { |
108 | 0 | ret = error_errno(_("unable to read alternates file")); |
109 | 0 | goto out; |
110 | 0 | } |
111 | | |
112 | 0 | for (size_t i = 0; i < opts->alternates->nr; i++) { |
113 | 0 | const char *alternate = opts->alternates->v[i]; |
114 | 0 | if (!strset_add(&seen, alternate)) |
115 | 0 | continue; |
116 | 0 | fprintf(alternates, "%s\n", alternate); |
117 | 0 | } |
118 | |
|
119 | 0 | if (ferror(alternates)) { |
120 | 0 | ret = error_errno(_("unable to write alternates file")); |
121 | 0 | goto out; |
122 | 0 | } |
123 | | |
124 | 0 | if (commit_lock_file(&alternates_lock)) { |
125 | 0 | ret = error_errno(_("unable to commit alternates file")); |
126 | 0 | goto out; |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | /* Reprepare the object database to activate alternates. */ |
131 | 0 | odb_reprepare(source->odb); |
132 | |
|
133 | 0 | ret = 0; |
134 | |
|
135 | 0 | out: |
136 | 0 | rollback_lock_file(&alternates_lock); |
137 | 0 | strbuf_release(&line); |
138 | 0 | strbuf_release(&path); |
139 | 0 | strset_clear(&seen); |
140 | 0 | return ret; |
141 | 0 | } |
142 | | |
143 | | static void odb_source_files_prepare(struct odb_source *source, |
144 | | enum odb_prepare_flags flags) |
145 | 0 | { |
146 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
147 | 0 | odb_source_prepare(&files->loose->base, flags); |
148 | 0 | odb_source_prepare(&files->packed->base, flags); |
149 | 0 | } |
150 | | |
151 | | static enum odb_read_status odb_source_files_read_object_info(struct odb_source *source, |
152 | | const struct object_id *oid, |
153 | | struct object_info *oi, |
154 | | enum object_info_flags flags, |
155 | | struct strbuf *errmsg) |
156 | 0 | { |
157 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
158 | 0 | enum odb_read_status ret_packed, ret_loose; |
159 | |
|
160 | 0 | ret_packed = odb_source_read_object_info(&files->packed->base, oid, oi, |
161 | 0 | flags, errmsg); |
162 | 0 | if (!ret_packed) |
163 | 0 | return 0; |
164 | | |
165 | 0 | ret_loose = odb_source_read_object_info(&files->loose->base, oid, oi, flags, |
166 | 0 | ret_packed == ODB_READ_NOT_FOUND ? errmsg : NULL); |
167 | 0 | if (!ret_loose) |
168 | 0 | return 0; |
169 | | |
170 | | /* |
171 | | * Reading the packed object may have failed even though the object |
172 | | * exists, for example because it is corrupt. Report this failure to |
173 | | * the caller in case neither of the sources was able to read the |
174 | | * object, and prefer the error of the packed source in case both |
175 | | * reads have failed. |
176 | | */ |
177 | 0 | if (ret_packed != ODB_READ_NOT_FOUND) |
178 | 0 | return ret_packed; |
179 | 0 | return ret_loose; |
180 | 0 | } |
181 | | |
182 | | static int odb_source_files_read_object_stream(struct odb_stream **out, |
183 | | struct odb_source *source, |
184 | | const struct object_id *oid) |
185 | 0 | { |
186 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
187 | 0 | if (!odb_source_read_object_stream(out, &files->packed->base, oid) || |
188 | 0 | !odb_source_read_object_stream(out, &files->loose->base, oid)) |
189 | 0 | return 0; |
190 | 0 | return -1; |
191 | 0 | } |
192 | | |
193 | | static int odb_source_files_for_each_object(struct odb_source *source, |
194 | | const struct object_info *request, |
195 | | odb_for_each_object_cb cb, |
196 | | void *cb_data, |
197 | | const struct odb_for_each_object_options *opts) |
198 | 0 | { |
199 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
200 | 0 | int ret; |
201 | |
|
202 | 0 | if (!(opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) { |
203 | 0 | ret = odb_source_for_each_object(&files->loose->base, request, cb, cb_data, opts); |
204 | 0 | if (ret) |
205 | 0 | return ret; |
206 | 0 | } |
207 | | |
208 | 0 | ret = odb_source_for_each_object(&files->packed->base, request, cb, cb_data, opts); |
209 | 0 | if (ret) |
210 | 0 | return ret; |
211 | | |
212 | 0 | return 0; |
213 | 0 | } |
214 | | |
215 | | static int odb_source_files_count_objects(struct odb_source *source, |
216 | | enum odb_count_objects_flags flags, |
217 | | unsigned long *out) |
218 | 0 | { |
219 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
220 | 0 | unsigned long count; |
221 | 0 | int ret; |
222 | |
|
223 | 0 | ret = odb_source_count_objects(&files->packed->base, flags, &count); |
224 | 0 | if (ret < 0) |
225 | 0 | goto out; |
226 | | |
227 | 0 | if (!(flags & ODB_COUNT_OBJECTS_APPROXIMATE)) { |
228 | 0 | unsigned long loose_count; |
229 | |
|
230 | 0 | ret = odb_source_count_objects(&files->loose->base, flags, &loose_count); |
231 | 0 | if (ret < 0) |
232 | 0 | goto out; |
233 | | |
234 | 0 | count += loose_count; |
235 | 0 | } |
236 | | |
237 | 0 | *out = count; |
238 | 0 | ret = 0; |
239 | |
|
240 | 0 | out: |
241 | 0 | return ret; |
242 | 0 | } |
243 | | |
244 | | static int odb_source_files_find_abbrev_len(struct odb_source *source, |
245 | | const struct object_id *oid, |
246 | | unsigned min_len, |
247 | | unsigned *out) |
248 | 0 | { |
249 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
250 | 0 | unsigned len = min_len; |
251 | 0 | int ret; |
252 | |
|
253 | 0 | ret = odb_source_find_abbrev_len(&files->packed->base, oid, len, &len); |
254 | 0 | if (ret < 0) |
255 | 0 | goto out; |
256 | | |
257 | 0 | ret = odb_source_find_abbrev_len(&files->loose->base, oid, len, &len); |
258 | 0 | if (ret < 0) |
259 | 0 | goto out; |
260 | | |
261 | 0 | *out = len; |
262 | 0 | ret = 0; |
263 | |
|
264 | 0 | out: |
265 | 0 | return ret; |
266 | 0 | } |
267 | | |
268 | | static int odb_source_files_freshen_object(struct odb_source *source, |
269 | | const struct object_id *oid, |
270 | | const time_t *mtime) |
271 | 0 | { |
272 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
273 | 0 | if (odb_source_freshen_object(&files->packed->base, oid, mtime) || |
274 | 0 | odb_source_freshen_object(&files->loose->base, oid, mtime)) |
275 | 0 | return 1; |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | | static int odb_source_files_write_object(struct odb_source *source, |
280 | | const void *buf, size_t len, |
281 | | enum object_type type, |
282 | | const struct object_id *oid, |
283 | | const struct object_id *compat_oid, |
284 | | const time_t *mtime, |
285 | | enum odb_write_object_flags flags) |
286 | 0 | { |
287 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
288 | 0 | return odb_source_write_object(&files->loose->base, buf, len, type, |
289 | 0 | oid, compat_oid, mtime, flags); |
290 | 0 | } |
291 | | |
292 | | static int odb_source_files_write_object_stream(struct odb_source *source, |
293 | | struct odb_stream *stream, |
294 | | struct object_id *oid) |
295 | 0 | { |
296 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
297 | 0 | return odb_source_write_object_stream(&files->loose->base, stream, oid); |
298 | 0 | } |
299 | | |
300 | | static int odb_source_files_begin_transaction(struct odb_source *source, |
301 | | struct odb_transaction **out, |
302 | | enum odb_transaction_flags flags) |
303 | 0 | { |
304 | 0 | return odb_transaction_files_begin(source, out, flags); |
305 | 0 | } |
306 | | |
307 | | static int odb_source_files_read_alternates(struct odb_source *source, |
308 | | struct strvec *out) |
309 | 0 | { |
310 | 0 | struct strbuf buf = STRBUF_INIT; |
311 | 0 | char *path; |
312 | |
|
313 | 0 | path = xstrfmt("%s/info/alternates", source->path); |
314 | 0 | if (strbuf_read_file(&buf, path, 1024) < 0) { |
315 | 0 | warn_on_fopen_errors(path); |
316 | 0 | free(path); |
317 | 0 | return 0; |
318 | 0 | } |
319 | 0 | parse_alternates(buf.buf, '\n', source->path, out); |
320 | |
|
321 | 0 | strbuf_release(&buf); |
322 | 0 | free(path); |
323 | 0 | return 0; |
324 | 0 | } |
325 | | |
326 | | static int too_many_loose_objects(struct odb_source_files *files, int limit) |
327 | 0 | { |
328 | 0 | unsigned long loose_count; |
329 | |
|
330 | 0 | if (limit <= 0) |
331 | 0 | return 0; |
332 | | |
333 | 0 | if (odb_source_count_objects(&files->loose->base, ODB_COUNT_OBJECTS_APPROXIMATE, |
334 | 0 | &loose_count) < 0) |
335 | 0 | return 0; |
336 | | |
337 | | /* |
338 | | * This is weird, but stems from legacy behaviour: the GC auto |
339 | | * threshold was always essentially interpreted as if it was rounded up |
340 | | * to the next multiple 256 of, so we retain this behaviour for now. |
341 | | */ |
342 | 0 | return loose_count > (DIV_ROUND_UP(((unsigned long) limit), 256) * 256); |
343 | 0 | } |
344 | | |
345 | | static struct packed_git *find_base_packs(struct odb_source_files *files, |
346 | | struct string_list *packs, |
347 | | unsigned long limit) |
348 | 0 | { |
349 | 0 | struct packfile_list_entry *e; |
350 | 0 | struct packed_git *base = NULL; |
351 | |
|
352 | 0 | for (e = packfile_store_get_packs(files->packed); e; e = e->next) { |
353 | 0 | if (e->pack->is_cruft) |
354 | 0 | continue; |
355 | 0 | if (limit) { |
356 | 0 | if ((uintmax_t) e->pack->pack_size >= limit) |
357 | 0 | string_list_append(packs, e->pack->pack_name); |
358 | 0 | } else if (!base || base->pack_size < e->pack->pack_size) { |
359 | 0 | base = e->pack; |
360 | 0 | } |
361 | 0 | } |
362 | |
|
363 | 0 | if (base) |
364 | 0 | string_list_append(packs, base->pack_name); |
365 | |
|
366 | 0 | return base; |
367 | 0 | } |
368 | | |
369 | | static int too_many_packs(struct odb_source_files *files, int gc_auto_pack_limit) |
370 | 0 | { |
371 | 0 | struct packfile_list_entry *e; |
372 | 0 | int cnt = 0; |
373 | |
|
374 | 0 | if (gc_auto_pack_limit <= 0) |
375 | 0 | return 0; |
376 | | |
377 | 0 | for (e = packfile_store_get_packs(files->packed); e; e = e->next) { |
378 | 0 | if (e->pack->pack_keep) |
379 | 0 | continue; |
380 | | /* |
381 | | * Perhaps check the size of the pack and count only |
382 | | * very small ones here? |
383 | | */ |
384 | 0 | cnt++; |
385 | 0 | } |
386 | 0 | return gc_auto_pack_limit < cnt; |
387 | 0 | } |
388 | | |
389 | | static uint64_t total_ram(void) |
390 | 0 | { |
391 | 0 | #if defined(HAVE_SYSINFO) |
392 | 0 | struct sysinfo si; |
393 | |
|
394 | 0 | if (!sysinfo(&si)) { |
395 | 0 | uint64_t total = si.totalram; |
396 | |
|
397 | 0 | if (si.mem_unit > 1) |
398 | 0 | total *= (uint64_t)si.mem_unit; |
399 | 0 | return total; |
400 | 0 | } |
401 | | #elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM) || defined(HW_PHYSMEM64)) |
402 | | uint64_t physical_memory; |
403 | | int mib[2]; |
404 | | size_t length; |
405 | | |
406 | | mib[0] = CTL_HW; |
407 | | # if defined(HW_MEMSIZE) |
408 | | mib[1] = HW_MEMSIZE; |
409 | | # elif defined(HW_PHYSMEM64) |
410 | | mib[1] = HW_PHYSMEM64; |
411 | | # else |
412 | | mib[1] = HW_PHYSMEM; |
413 | | # endif |
414 | | length = sizeof(physical_memory); |
415 | | if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) { |
416 | | if (length == 4) { |
417 | | uint32_t mem; |
418 | | |
419 | | if (!sysctl(mib, 2, &mem, &length, NULL, 0)) |
420 | | physical_memory = mem; |
421 | | } |
422 | | return physical_memory; |
423 | | } |
424 | | #elif defined(GIT_WINDOWS_NATIVE) |
425 | | MEMORYSTATUSEX memInfo; |
426 | | |
427 | | memInfo.dwLength = sizeof(MEMORYSTATUSEX); |
428 | | if (GlobalMemoryStatusEx(&memInfo)) |
429 | | return memInfo.ullTotalPhys; |
430 | | #endif |
431 | 0 | return 0; |
432 | 0 | } |
433 | | |
434 | | static uint64_t estimate_repack_memory(struct odb_source_files *files, |
435 | | struct packed_git *pack) |
436 | 0 | { |
437 | 0 | unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; |
438 | 0 | unsigned long delta_base_cache_limit = DEFAULT_DELTA_BASE_CACHE_LIMIT; |
439 | 0 | unsigned long nr_objects; |
440 | 0 | size_t os_cache, heap; |
441 | |
|
442 | 0 | if (odb_source_count_objects(&files->base, ODB_COUNT_OBJECTS_APPROXIMATE, |
443 | 0 | &nr_objects) < 0) |
444 | 0 | return 0; |
445 | | |
446 | 0 | if (!pack || !nr_objects) |
447 | 0 | return 0; |
448 | | |
449 | 0 | repo_config_get_ulong(files->base.odb->repo, "pack.deltacachesize", |
450 | 0 | &max_delta_cache_size); |
451 | 0 | repo_config_get_ulong(files->base.odb->repo, "core.deltabasecachelimit", |
452 | 0 | &delta_base_cache_limit); |
453 | | |
454 | | /* |
455 | | * First we have to scan through at least one pack. |
456 | | * Assume enough room in OS file cache to keep the entire pack |
457 | | * or we may accidentally evict data of other processes from |
458 | | * the cache. |
459 | | */ |
460 | 0 | os_cache = pack->pack_size + pack->index_size; |
461 | | /* then pack-objects needs lots more for book keeping */ |
462 | 0 | heap = sizeof(struct object_entry) * nr_objects; |
463 | | /* |
464 | | * internal rev-list --all --objects takes up some memory too, |
465 | | * let's say half of it is for blobs |
466 | | */ |
467 | 0 | heap += sizeof(struct blob) * nr_objects / 2; |
468 | | /* |
469 | | * and the other half is for trees (commits and tags are |
470 | | * usually insignificant) |
471 | | */ |
472 | 0 | heap += sizeof(struct tree) * nr_objects / 2; |
473 | | /* and then obj_hash[], underestimated in fact */ |
474 | 0 | heap += sizeof(struct object *) * nr_objects; |
475 | | /* revindex is used also */ |
476 | 0 | heap += (sizeof(off_t) + sizeof(uint32_t)) * nr_objects; |
477 | | /* |
478 | | * read_sha1_file() (either at delta calculation phase, or |
479 | | * writing phase) also fills up the delta base cache |
480 | | */ |
481 | 0 | heap += delta_base_cache_limit; |
482 | | /* and of course pack-objects has its own delta cache */ |
483 | 0 | heap += max_delta_cache_size; |
484 | |
|
485 | 0 | return os_cache + heap; |
486 | 0 | } |
487 | | |
488 | | static int keep_one_pack(struct string_list_item *item, void *data) |
489 | 0 | { |
490 | 0 | struct strvec *args = data; |
491 | 0 | strvec_pushf(args, "--keep-pack=%s", basename(item->string)); |
492 | 0 | return 0; |
493 | 0 | } |
494 | | |
495 | | static void add_repack_all_option(struct repository *repo, |
496 | | const struct odb_optimize_options *opts, |
497 | | struct string_list *keep_pack, |
498 | | struct strvec *args) |
499 | 0 | { |
500 | 0 | char *repack_filter = NULL; |
501 | 0 | char *repack_filter_to = NULL; |
502 | |
|
503 | 0 | repo_config_get_string(repo, "gc.repackfilter", &repack_filter); |
504 | 0 | repo_config_get_string(repo, "gc.repackfilterto", &repack_filter_to); |
505 | |
|
506 | 0 | if (opts->prune_expire && !strcmp(opts->prune_expire, "now") && |
507 | 0 | !(opts->cruft_packs && opts->expire_to)) |
508 | 0 | strvec_push(args, "-a"); |
509 | 0 | else if (opts->cruft_packs) { |
510 | 0 | strvec_push(args, "--cruft"); |
511 | 0 | if (opts->prune_expire) |
512 | 0 | strvec_pushf(args, "--cruft-expiration=%s", opts->prune_expire); |
513 | 0 | if (opts->max_cruft_size) |
514 | 0 | strvec_pushf(args, "--max-cruft-size=%lu", |
515 | 0 | opts->max_cruft_size); |
516 | 0 | if (opts->expire_to) |
517 | 0 | strvec_pushf(args, "--expire-to=%s", opts->expire_to); |
518 | 0 | } else { |
519 | 0 | strvec_push(args, "-A"); |
520 | 0 | if (opts->prune_expire) |
521 | 0 | strvec_pushf(args, "--unpack-unreachable=%s", opts->prune_expire); |
522 | 0 | } |
523 | |
|
524 | 0 | if (keep_pack) |
525 | 0 | for_each_string_list(keep_pack, keep_one_pack, args); |
526 | |
|
527 | 0 | if (repack_filter && *repack_filter) |
528 | 0 | strvec_pushf(args, "--filter=%s", repack_filter); |
529 | 0 | if (repack_filter_to && *repack_filter_to) |
530 | 0 | strvec_pushf(args, "--filter-to=%s", repack_filter_to); |
531 | |
|
532 | 0 | free(repack_filter); |
533 | 0 | free(repack_filter_to); |
534 | 0 | } |
535 | | |
536 | | static void add_repack_incremental_option(struct strvec *args) |
537 | 0 | { |
538 | 0 | strvec_push(args, "--no-write-bitmap-index"); |
539 | 0 | } |
540 | | |
541 | | bool odb_source_files_optimize_required(struct odb_source *source, |
542 | | const struct odb_optimize_options *opts) |
543 | 0 | { |
544 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
545 | 0 | struct repository *repo = source->odb->repo; |
546 | |
|
547 | 0 | switch (opts->strategy) { |
548 | 0 | case ODB_OPTIMIZE_INCREMENTAL: { |
549 | 0 | int gc_auto_threshold = 6700; |
550 | 0 | int gc_auto_pack_limit = 50; |
551 | |
|
552 | 0 | repo_config_get_int(repo, "gc.auto", &gc_auto_threshold); |
553 | 0 | repo_config_get_int(repo, "gc.autopacklimit", &gc_auto_pack_limit); |
554 | | |
555 | | /* |
556 | | * Setting gc.auto to 0 or negative can disable the |
557 | | * automatic gc. |
558 | | */ |
559 | 0 | if (gc_auto_threshold <= 0) |
560 | 0 | return false; |
561 | 0 | if (!too_many_packs(files, gc_auto_pack_limit) && |
562 | 0 | !too_many_loose_objects(files, gc_auto_threshold)) |
563 | 0 | return false; |
564 | | |
565 | 0 | return true; |
566 | 0 | } |
567 | 0 | case ODB_OPTIMIZE_GEOMETRIC: { |
568 | 0 | struct pack_geometry geometry = { |
569 | 0 | .split_factor = 2, |
570 | 0 | }; |
571 | 0 | struct pack_objects_args po_args = { |
572 | 0 | .local = 1, |
573 | 0 | }; |
574 | 0 | struct existing_packs existing_packs = EXISTING_PACKS_INIT; |
575 | 0 | struct string_list kept_packs = STRING_LIST_INIT_DUP; |
576 | 0 | int auto_value = 6700; |
577 | 0 | bool ret; |
578 | |
|
579 | 0 | repo_config_get_int(repo, "maintenance.geometric-repack.auto", |
580 | 0 | &auto_value); |
581 | 0 | if (!auto_value) |
582 | 0 | return false; |
583 | 0 | if (auto_value < 0) |
584 | 0 | return true; |
585 | | |
586 | 0 | repo_config_get_int(repo, "maintenance.geometric-repack.splitFactor", |
587 | 0 | &geometry.split_factor); |
588 | |
|
589 | 0 | existing_packs.repo = repo; |
590 | 0 | existing_packs_collect(&existing_packs, &kept_packs); |
591 | 0 | pack_geometry_init(&geometry, &existing_packs, &po_args); |
592 | 0 | pack_geometry_split(&geometry); |
593 | | |
594 | | /* |
595 | | * When we'd merge at least two packs with one another we always |
596 | | * perform the repack. |
597 | | */ |
598 | 0 | if (geometry.split) { |
599 | 0 | ret = true; |
600 | 0 | goto out; |
601 | 0 | } |
602 | | |
603 | | /* |
604 | | * Otherwise, we estimate the number of loose objects to determine |
605 | | * whether we want to create a new packfile or not. |
606 | | */ |
607 | 0 | if (too_many_loose_objects(files, auto_value)) { |
608 | 0 | ret = true; |
609 | 0 | goto out; |
610 | 0 | } |
611 | | |
612 | 0 | ret = false; |
613 | |
|
614 | 0 | out: |
615 | 0 | existing_packs_release(&existing_packs); |
616 | 0 | pack_geometry_release(&geometry); |
617 | 0 | return ret; |
618 | 0 | } |
619 | 0 | default: |
620 | 0 | BUG("unknown maintenance strategy '%d'", opts->strategy); |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | | int odb_source_files_optimize(struct odb_source *source, |
625 | | const struct odb_optimize_options *opts) |
626 | 0 | { |
627 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
628 | 0 | struct repository *repo = source->odb->repo; |
629 | 0 | struct child_process repack_cmd = CHILD_PROCESS_INIT; |
630 | 0 | unsigned long big_pack_threshold = 0; |
631 | 0 | int gc_auto_threshold = 6700; |
632 | 0 | int gc_auto_pack_limit = 50; |
633 | 0 | int ret; |
634 | |
|
635 | 0 | repo_config_get_int(repo, "gc.auto", &gc_auto_threshold); |
636 | 0 | repo_config_get_int(repo, "gc.autopacklimit", &gc_auto_pack_limit); |
637 | 0 | repo_config_get_ulong(repo, "gc.bigpackthreshold", &big_pack_threshold); |
638 | |
|
639 | 0 | if (repo->repository_format_precious_objects) |
640 | 0 | return 0; |
641 | | |
642 | 0 | repack_cmd.git_cmd = 1; |
643 | 0 | repack_cmd.odb_to_close = repo->objects; |
644 | |
|
645 | 0 | strvec_pushl(&repack_cmd.args, "repack", "-d", "-l", NULL); |
646 | 0 | if (opts->flags & ODB_OPTIMIZE_NO_REUSE_DELTAS) |
647 | 0 | strvec_push(&repack_cmd.args, "-f"); |
648 | 0 | if (opts->depth > 0) |
649 | 0 | strvec_pushf(&repack_cmd.args, "--depth=%d", opts->depth); |
650 | 0 | if (opts->window > 0) |
651 | 0 | strvec_pushf(&repack_cmd.args, "--window=%d", opts->window); |
652 | 0 | if (!(opts->flags & ODB_OPTIMIZE_VERBOSE)) |
653 | 0 | strvec_push(&repack_cmd.args, "-q"); |
654 | | |
655 | | /* |
656 | | * There's three cases we need to consider: |
657 | | * |
658 | | * - If we're invoked without `--auto` we'll need to perform a full |
659 | | * repack. |
660 | | * |
661 | | * - If we're invoked with `--auto` and there's too many packs, then |
662 | | * we perform a full repack, as well. |
663 | | * |
664 | | * - Otherwise we perform an incremental repack. |
665 | | */ |
666 | 0 | switch (opts->strategy) { |
667 | 0 | case ODB_OPTIMIZE_INCREMENTAL: |
668 | 0 | if (!(opts->flags & ODB_OPTIMIZE_AUTO)) { |
669 | 0 | struct string_list keep_pack = STRING_LIST_INIT_NODUP; |
670 | |
|
671 | 0 | if (opts->keep_largest_pack != -1) { |
672 | 0 | if (opts->keep_largest_pack) |
673 | 0 | find_base_packs(files, &keep_pack, 0); |
674 | 0 | } else if (big_pack_threshold) { |
675 | 0 | find_base_packs(files, &keep_pack, big_pack_threshold); |
676 | 0 | } |
677 | |
|
678 | 0 | add_repack_all_option(repo, opts, &keep_pack, &repack_cmd.args); |
679 | 0 | string_list_clear(&keep_pack, 0); |
680 | 0 | } else { |
681 | 0 | if (too_many_packs(files, gc_auto_pack_limit)) { |
682 | 0 | struct string_list keep_pack = STRING_LIST_INIT_NODUP; |
683 | |
|
684 | 0 | if (big_pack_threshold) { |
685 | 0 | find_base_packs(files, &keep_pack, big_pack_threshold); |
686 | 0 | if (keep_pack.nr >= (unsigned long) gc_auto_pack_limit) { |
687 | 0 | string_list_clear(&keep_pack, 0); |
688 | 0 | find_base_packs(files, &keep_pack, 0); |
689 | 0 | } |
690 | 0 | } else { |
691 | 0 | struct packed_git *p = find_base_packs(files, &keep_pack, 0); |
692 | 0 | uint64_t mem_have, mem_want; |
693 | |
|
694 | 0 | mem_have = total_ram(); |
695 | 0 | mem_want = estimate_repack_memory(files, p); |
696 | | |
697 | | /* |
698 | | * Only allow 1/2 of memory for pack-objects, leave |
699 | | * the rest for the OS and other processes in the |
700 | | * system. |
701 | | */ |
702 | 0 | if (!mem_have || mem_want < mem_have / 2) |
703 | 0 | string_list_clear(&keep_pack, 0); |
704 | 0 | } |
705 | |
|
706 | 0 | add_repack_all_option(repo, opts, &keep_pack, &repack_cmd.args); |
707 | 0 | string_list_clear(&keep_pack, 0); |
708 | 0 | } else { |
709 | 0 | add_repack_incremental_option(&repack_cmd.args); |
710 | 0 | } |
711 | 0 | } |
712 | |
|
713 | 0 | break; |
714 | 0 | case ODB_OPTIMIZE_GEOMETRIC: { |
715 | 0 | struct pack_geometry geometry = { |
716 | 0 | .split_factor = 2, |
717 | 0 | }; |
718 | 0 | struct pack_objects_args po_args = { |
719 | 0 | .local = 1, |
720 | 0 | }; |
721 | 0 | struct existing_packs existing_packs = EXISTING_PACKS_INIT; |
722 | 0 | struct string_list kept_packs = STRING_LIST_INIT_DUP; |
723 | |
|
724 | 0 | repo_config_get_int(repo, "maintenance.geometric-repack.splitFactor", |
725 | 0 | &geometry.split_factor); |
726 | |
|
727 | 0 | existing_packs.repo = repo; |
728 | 0 | existing_packs_collect(&existing_packs, &kept_packs); |
729 | 0 | pack_geometry_init(&geometry, &existing_packs, &po_args); |
730 | 0 | pack_geometry_split(&geometry); |
731 | |
|
732 | 0 | if (geometry.split < geometry.pack_nr) { |
733 | 0 | strvec_pushf(&repack_cmd.args, "--geometric=%d", |
734 | 0 | geometry.split_factor); |
735 | 0 | } else { |
736 | 0 | add_repack_all_option(repo, opts, NULL, &repack_cmd.args); |
737 | 0 | } |
738 | 0 | if (repo->settings.core_multi_pack_index) |
739 | 0 | strvec_push(&repack_cmd.args, "--write-midx"); |
740 | |
|
741 | 0 | existing_packs_release(&existing_packs); |
742 | 0 | pack_geometry_release(&geometry); |
743 | 0 | break; |
744 | 0 | } |
745 | 0 | default: |
746 | 0 | die("unknown maintenance strategy '%d'", opts->strategy); |
747 | 0 | } |
748 | | |
749 | 0 | if (run_command(&repack_cmd)) { |
750 | 0 | ret = error("failed to run %s", repack_cmd.args.v[0]); |
751 | 0 | goto out; |
752 | 0 | } |
753 | | |
754 | | /* Geometric repacking uses cruft packs, so we don't have to prune separately. */ |
755 | 0 | if (opts->strategy != ODB_OPTIMIZE_GEOMETRIC && opts->prune_expire) { |
756 | 0 | struct child_process prune_cmd = CHILD_PROCESS_INIT; |
757 | |
|
758 | 0 | strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL); |
759 | | /* run `git prune` even if using cruft packs */ |
760 | 0 | strvec_push(&prune_cmd.args, opts->prune_expire); |
761 | 0 | if (!(opts->flags & ODB_OPTIMIZE_VERBOSE)) |
762 | 0 | strvec_push(&prune_cmd.args, "--no-progress"); |
763 | 0 | if (repo_has_promisor_remote(repo)) |
764 | 0 | strvec_push(&prune_cmd.args, |
765 | 0 | "--exclude-promisor-objects"); |
766 | 0 | prune_cmd.git_cmd = 1; |
767 | |
|
768 | 0 | if (run_command(&prune_cmd)) { |
769 | 0 | ret = error("failed to run %s", prune_cmd.args.v[0]); |
770 | 0 | goto out; |
771 | 0 | } |
772 | 0 | } |
773 | | |
774 | 0 | if (opts->flags & ODB_OPTIMIZE_AUTO && too_many_loose_objects(files, gc_auto_threshold)) |
775 | 0 | warning(_("There are too many unreachable loose objects; " |
776 | 0 | "run 'git prune' to remove them.")); |
777 | |
|
778 | 0 | ret = 0; |
779 | |
|
780 | 0 | out: |
781 | 0 | return ret; |
782 | 0 | } |
783 | | |
784 | | struct odb_pack_generator_files { |
785 | | struct odb_pack_generator base; |
786 | | struct child_process cp; |
787 | | }; |
788 | | |
789 | | static int odb_pack_generator_files_finish(struct odb_pack_generator *_generator) |
790 | 0 | { |
791 | 0 | struct odb_pack_generator_files *generator = |
792 | 0 | (struct odb_pack_generator_files *)_generator; |
793 | 0 | int ret; |
794 | |
|
795 | 0 | ret = finish_command(&generator->cp); |
796 | 0 | free(generator); |
797 | |
|
798 | 0 | if (ret) { |
799 | | /* |
800 | | * On failure, pack-objects is expected to have written a |
801 | | * useful error message to its standard error stream already. |
802 | | * Death by signal is worth mentioning, though, with the |
803 | | * exception of SIGPIPE: that is a normal occurrence when the |
804 | | * consumer of the pack hangs up. |
805 | | */ |
806 | 0 | if (ret > 128 && ret - 128 == SIGPIPE) |
807 | 0 | return -1; |
808 | 0 | if (ret > 128) |
809 | 0 | error(_("pack-objects died of signal %d"), ret - 128); |
810 | 0 | return -1; |
811 | 0 | } |
812 | | |
813 | 0 | return 0; |
814 | 0 | } |
815 | | |
816 | | static int odb_source_files_generate_pack(struct odb_source *source UNUSED, |
817 | | struct odb_pack_generator **out, |
818 | | const struct odb_generate_pack_options *opts) |
819 | 0 | { |
820 | 0 | struct odb_pack_generator_files *generator; |
821 | 0 | struct child_process *cp; |
822 | 0 | FILE *in; |
823 | |
|
824 | 0 | CALLOC_ARRAY(generator, 1); |
825 | 0 | child_process_init(&generator->cp); |
826 | 0 | cp = &generator->cp; |
827 | | |
828 | | /* |
829 | | * The hook is expected to spawn "$hook git pack-objects <args...>" |
830 | | * and to behave like git-pack-objects(1) would have. This can for |
831 | | * example be used to serve precomputed packfiles. |
832 | | */ |
833 | 0 | if (opts->pack_objects_hook) { |
834 | 0 | strvec_push(&cp->args, opts->pack_objects_hook); |
835 | 0 | strvec_push(&cp->args, "git"); |
836 | 0 | cp->use_shell = 1; |
837 | 0 | } else { |
838 | 0 | cp->git_cmd = 1; |
839 | 0 | } |
840 | | |
841 | | /* |
842 | | * The caller-provided shallow boundary overrides any shallow state |
843 | | * that the repository itself may have, so the shallow file needs to |
844 | | * be neutralized. |
845 | | */ |
846 | 0 | if (opts->shallows.nr) { |
847 | 0 | strvec_push(&cp->args, "--shallow-file"); |
848 | 0 | strvec_push(&cp->args, ""); |
849 | 0 | } |
850 | 0 | strvec_push(&cp->args, "pack-objects"); |
851 | 0 | strvec_push(&cp->args, "--revs"); |
852 | 0 | strvec_push(&cp->args, "--stdout"); |
853 | 0 | if (opts->thin) |
854 | 0 | strvec_push(&cp->args, "--thin"); |
855 | 0 | if (opts->shallow) |
856 | 0 | strvec_push(&cp->args, "--shallow"); |
857 | 0 | if (opts->ofs_delta) |
858 | 0 | strvec_push(&cp->args, "--delta-base-offset"); |
859 | 0 | if (opts->include_tag) |
860 | 0 | strvec_push(&cp->args, "--include-tag"); |
861 | 0 | if (opts->missing_allow_promisor) |
862 | 0 | strvec_push(&cp->args, "--missing=allow-promisor"); |
863 | 0 | if (opts->disable_bitmaps) |
864 | 0 | strvec_push(&cp->args, "--no-use-bitmap-index"); |
865 | 0 | switch (opts->progress) { |
866 | 0 | case ODB_GENERATE_PACK_PROGRESS_NONE: |
867 | 0 | strvec_push(&cp->args, "--quiet"); |
868 | 0 | break; |
869 | 0 | case ODB_GENERATE_PACK_PROGRESS_STANDARD: |
870 | 0 | strvec_push(&cp->args, "--progress"); |
871 | 0 | break; |
872 | 0 | case ODB_GENERATE_PACK_PROGRESS_VERBOSE: |
873 | 0 | strvec_push(&cp->args, "--all-progress"); |
874 | 0 | break; |
875 | 0 | default: |
876 | 0 | BUG("unknown progress option %d", opts->progress); |
877 | 0 | } |
878 | 0 | if (opts->filter_spec) |
879 | 0 | strvec_pushf(&cp->args, "--filter=%s", opts->filter_spec); |
880 | 0 | if (opts->uri_protocols) |
881 | 0 | for (size_t i = 0; i < opts->uri_protocols->nr; i++) |
882 | 0 | strvec_pushf(&cp->args, "--uri-protocol=%s", |
883 | 0 | opts->uri_protocols->items[i].string); |
884 | |
|
885 | 0 | cp->in = -1; |
886 | 0 | cp->out = opts->pack_fd; |
887 | 0 | cp->err = opts->progress_fd; |
888 | 0 | cp->clean_on_exit = 1; |
889 | |
|
890 | 0 | if (start_command(cp)) { |
891 | 0 | free(generator); |
892 | 0 | return error(_("could not spawn pack-objects")); |
893 | 0 | } |
894 | | |
895 | | /* |
896 | | * Feed the objects to pack-objects. This is safe to do synchronously |
897 | | * because pack-objects consumes all of its standard input before it |
898 | | * starts to generate the pack. |
899 | | */ |
900 | 0 | in = xfdopen(cp->in, "w"); |
901 | 0 | for (size_t i = 0; i < opts->shallows.nr; i++) |
902 | 0 | fprintf(in, "--shallow %s\n", oid_to_hex(&opts->shallows.oid[i])); |
903 | 0 | for (size_t i = 0; i < opts->wants.nr; i++) |
904 | 0 | fprintf(in, "%s\n", oid_to_hex(&opts->wants.oid[i])); |
905 | 0 | fprintf(in, "--not\n"); |
906 | 0 | for (size_t i = 0; i < opts->haves.nr; i++) |
907 | 0 | fprintf(in, "%s\n", oid_to_hex(&opts->haves.oid[i])); |
908 | 0 | fprintf(in, "\n"); |
909 | 0 | fflush(in); |
910 | 0 | if (ferror(in)) { |
911 | 0 | error(_("error writing to pack-objects")); |
912 | 0 | fclose(in); |
913 | 0 | if (opts->pack_fd < 0) |
914 | 0 | close(cp->out); |
915 | 0 | if (opts->progress_fd < 0) |
916 | 0 | close(cp->err); |
917 | 0 | finish_command(cp); |
918 | 0 | free(generator); |
919 | 0 | return -1; |
920 | 0 | } |
921 | 0 | fclose(in); |
922 | |
|
923 | 0 | generator->base.out = opts->pack_fd < 0 ? cp->out : -1; |
924 | 0 | generator->base.err = opts->progress_fd < 0 ? cp->err : -1; |
925 | 0 | generator->base.finish = odb_pack_generator_files_finish; |
926 | |
|
927 | 0 | *out = &generator->base; |
928 | 0 | return 0; |
929 | 0 | } |
930 | | |
931 | | static int odb_source_files_fsck(struct odb_source *source, |
932 | | struct odb_fsck_options *opts) |
933 | 0 | { |
934 | 0 | struct odb_source_files *files = odb_source_files_downcast(source); |
935 | 0 | int ret = 0; |
936 | |
|
937 | 0 | if (!(opts->flags & ODB_FSCK_FULL) && !source->local) |
938 | 0 | return 0; |
939 | | |
940 | 0 | ret |= odb_source_fsck(&files->loose->base, opts); |
941 | 0 | ret |= odb_source_fsck(&files->packed->base, opts); |
942 | |
|
943 | 0 | return ret; |
944 | 0 | } |
945 | | |
946 | | struct odb_source_files *odb_source_files_new(struct object_database *odb, |
947 | | const char *path, |
948 | | bool local) |
949 | 0 | { |
950 | 0 | struct odb_source_files *files; |
951 | |
|
952 | 0 | CALLOC_ARRAY(files, 1); |
953 | 0 | odb_source_init(&files->base, odb, ODB_SOURCE_FILES, path, local); |
954 | 0 | files->loose = odb_source_loose_new(odb, path, local); |
955 | 0 | files->packed = odb_source_packed_new(odb, path, local); |
956 | |
|
957 | 0 | files->base.free = odb_source_files_free; |
958 | 0 | files->base.close = odb_source_files_close; |
959 | 0 | files->base.create_on_disk = odb_source_files_create_on_disk; |
960 | 0 | files->base.prepare = odb_source_files_prepare; |
961 | 0 | files->base.fsck = odb_source_files_fsck; |
962 | 0 | files->base.read_object_info = odb_source_files_read_object_info; |
963 | 0 | files->base.read_object_stream = odb_source_files_read_object_stream; |
964 | 0 | files->base.for_each_object = odb_source_files_for_each_object; |
965 | 0 | files->base.count_objects = odb_source_files_count_objects; |
966 | 0 | files->base.find_abbrev_len = odb_source_files_find_abbrev_len; |
967 | 0 | files->base.freshen_object = odb_source_files_freshen_object; |
968 | 0 | files->base.write_object = odb_source_files_write_object; |
969 | 0 | files->base.write_object_stream = odb_source_files_write_object_stream; |
970 | 0 | files->base.begin_transaction = odb_source_files_begin_transaction; |
971 | 0 | files->base.read_alternates = odb_source_files_read_alternates; |
972 | 0 | files->base.optimize = odb_source_files_optimize; |
973 | 0 | files->base.optimize_required = odb_source_files_optimize_required; |
974 | 0 | files->base.generate_pack = odb_source_files_generate_pack; |
975 | | |
976 | | /* |
977 | | * Ideally, we would only ever store absolute paths in the source. This |
978 | | * is not (yet) possible though because we access and assume relative |
979 | | * paths in the primary ODB source in some user-facing functionality. |
980 | | */ |
981 | 0 | if (!is_absolute_path(path)) |
982 | 0 | chdir_notify_register(odb_source_files_reparent, files); |
983 | |
|
984 | 0 | return files; |
985 | 0 | } |