Line | Count | Source (jump to first uncovered line) |
1 | | #include "builtin.h" |
2 | | #include "gettext.h" |
3 | | #include "hex.h" |
4 | | #include "repository.h" |
5 | | #include "config.h" |
6 | | #include "commit.h" |
7 | | #include "tree.h" |
8 | | #include "blob.h" |
9 | | #include "tag.h" |
10 | | #include "refs.h" |
11 | | #include "pack.h" |
12 | | #include "cache-tree.h" |
13 | | #include "fsck.h" |
14 | | #include "parse-options.h" |
15 | | #include "progress.h" |
16 | | #include "streaming.h" |
17 | | #include "packfile.h" |
18 | | #include "object-file.h" |
19 | | #include "object-name.h" |
20 | | #include "object-store-ll.h" |
21 | | #include "path.h" |
22 | | #include "read-cache-ll.h" |
23 | | #include "replace-object.h" |
24 | | #include "resolve-undo.h" |
25 | | #include "run-command.h" |
26 | | #include "sparse-index.h" |
27 | | #include "worktree.h" |
28 | | #include "pack-revindex.h" |
29 | | #include "pack-bitmap.h" |
30 | | |
31 | 0 | #define REACHABLE 0x0001 |
32 | 0 | #define SEEN 0x0002 |
33 | 0 | #define HAS_OBJ 0x0004 |
34 | | /* This flag is set if something points to this object. */ |
35 | 0 | #define USED 0x0008 |
36 | | |
37 | | static int show_root; |
38 | | static int show_tags; |
39 | | static int show_unreachable; |
40 | | static int include_reflogs = 1; |
41 | | static int check_full = 1; |
42 | | static int connectivity_only; |
43 | | static int check_strict; |
44 | | static int keep_cache_objects; |
45 | | static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT; |
46 | | static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT; |
47 | | static int errors_found; |
48 | | static int write_lost_and_found; |
49 | | static int verbose; |
50 | | static int show_progress = -1; |
51 | | static int show_dangling = 1; |
52 | | static int name_objects; |
53 | 0 | #define ERROR_OBJECT 01 |
54 | 0 | #define ERROR_REACHABLE 02 |
55 | 0 | #define ERROR_PACK 04 |
56 | 0 | #define ERROR_REFS 010 |
57 | 0 | #define ERROR_COMMIT_GRAPH 020 |
58 | 0 | #define ERROR_MULTI_PACK_INDEX 040 |
59 | 0 | #define ERROR_PACK_REV_INDEX 0100 |
60 | 0 | #define ERROR_BITMAP 0200 |
61 | | |
62 | | static const char *describe_object(const struct object_id *oid) |
63 | 0 | { |
64 | 0 | return fsck_describe_object(&fsck_walk_options, oid); |
65 | 0 | } |
66 | | |
67 | | static const char *printable_type(const struct object_id *oid, |
68 | | enum object_type type) |
69 | 0 | { |
70 | 0 | const char *ret; |
71 | |
|
72 | 0 | if (type == OBJ_NONE) |
73 | 0 | type = oid_object_info(the_repository, oid, NULL); |
74 | |
|
75 | 0 | ret = type_name(type); |
76 | 0 | if (!ret) |
77 | 0 | ret = _("unknown"); |
78 | |
|
79 | 0 | return ret; |
80 | 0 | } |
81 | | |
82 | | static int objerror(struct object *obj, const char *err) |
83 | 0 | { |
84 | 0 | errors_found |= ERROR_OBJECT; |
85 | | /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ |
86 | 0 | fprintf_ln(stderr, _("error in %s %s: %s"), |
87 | 0 | printable_type(&obj->oid, obj->type), |
88 | 0 | describe_object(&obj->oid), err); |
89 | 0 | return -1; |
90 | 0 | } |
91 | | |
92 | | static int fsck_objects_error_func(struct fsck_options *o UNUSED, |
93 | | void *fsck_report, |
94 | | enum fsck_msg_type msg_type, |
95 | | enum fsck_msg_id msg_id UNUSED, |
96 | | const char *message) |
97 | 0 | { |
98 | 0 | struct fsck_object_report *report = fsck_report; |
99 | 0 | const struct object_id *oid = report->oid; |
100 | 0 | enum object_type object_type = report->object_type; |
101 | |
|
102 | 0 | switch (msg_type) { |
103 | 0 | case FSCK_WARN: |
104 | | /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */ |
105 | 0 | fprintf_ln(stderr, _("warning in %s %s: %s"), |
106 | 0 | printable_type(oid, object_type), |
107 | 0 | describe_object(oid), message); |
108 | 0 | return 0; |
109 | 0 | case FSCK_ERROR: |
110 | | /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ |
111 | 0 | fprintf_ln(stderr, _("error in %s %s: %s"), |
112 | 0 | printable_type(oid, object_type), |
113 | 0 | describe_object(oid), message); |
114 | 0 | return 1; |
115 | 0 | default: |
116 | 0 | BUG("%d (FSCK_IGNORE?) should never trigger this callback", |
117 | 0 | msg_type); |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | static struct object_array pending; |
122 | | |
123 | | static int mark_object(struct object *obj, enum object_type type, |
124 | | void *data, struct fsck_options *options UNUSED) |
125 | 0 | { |
126 | 0 | struct object *parent = data; |
127 | | |
128 | | /* |
129 | | * The only case data is NULL or type is OBJ_ANY is when |
130 | | * mark_object_reachable() calls us. All the callers of |
131 | | * that function has non-NULL obj hence ... |
132 | | */ |
133 | 0 | if (!obj) { |
134 | | /* ... these references to parent->fld are safe here */ |
135 | 0 | printf_ln(_("broken link from %7s %s"), |
136 | 0 | printable_type(&parent->oid, parent->type), |
137 | 0 | describe_object(&parent->oid)); |
138 | 0 | printf_ln(_("broken link from %7s %s"), |
139 | 0 | (type == OBJ_ANY ? _("unknown") : type_name(type)), |
140 | 0 | _("unknown")); |
141 | 0 | errors_found |= ERROR_REACHABLE; |
142 | 0 | return 1; |
143 | 0 | } |
144 | | |
145 | 0 | if (type != OBJ_ANY && obj->type != type) |
146 | | /* ... and the reference to parent is safe here */ |
147 | 0 | objerror(parent, _("wrong object type in link")); |
148 | |
|
149 | 0 | if (obj->flags & REACHABLE) |
150 | 0 | return 0; |
151 | 0 | obj->flags |= REACHABLE; |
152 | |
|
153 | 0 | if (is_promisor_object(&obj->oid)) |
154 | | /* |
155 | | * Further recursion does not need to be performed on this |
156 | | * object since it is a promisor object (so it does not need to |
157 | | * be added to "pending"). |
158 | | */ |
159 | 0 | return 0; |
160 | | |
161 | 0 | if (!(obj->flags & HAS_OBJ)) { |
162 | 0 | if (parent && !has_object(the_repository, &obj->oid, 1)) { |
163 | 0 | printf_ln(_("broken link from %7s %s\n" |
164 | 0 | " to %7s %s"), |
165 | 0 | printable_type(&parent->oid, parent->type), |
166 | 0 | describe_object(&parent->oid), |
167 | 0 | printable_type(&obj->oid, obj->type), |
168 | 0 | describe_object(&obj->oid)); |
169 | 0 | errors_found |= ERROR_REACHABLE; |
170 | 0 | } |
171 | 0 | return 1; |
172 | 0 | } |
173 | | |
174 | 0 | add_object_array(obj, NULL, &pending); |
175 | 0 | return 0; |
176 | 0 | } |
177 | | |
178 | | static void mark_object_reachable(struct object *obj) |
179 | 0 | { |
180 | 0 | mark_object(obj, OBJ_ANY, NULL, NULL); |
181 | 0 | } |
182 | | |
183 | | static int traverse_one_object(struct object *obj) |
184 | 0 | { |
185 | 0 | int result = fsck_walk(obj, obj, &fsck_walk_options); |
186 | |
|
187 | 0 | if (obj->type == OBJ_TREE) { |
188 | 0 | struct tree *tree = (struct tree *)obj; |
189 | 0 | free_tree_buffer(tree); |
190 | 0 | } |
191 | 0 | return result; |
192 | 0 | } |
193 | | |
194 | | static int traverse_reachable(void) |
195 | 0 | { |
196 | 0 | struct progress *progress = NULL; |
197 | 0 | unsigned int nr = 0; |
198 | 0 | int result = 0; |
199 | 0 | if (show_progress) |
200 | 0 | progress = start_delayed_progress(_("Checking connectivity"), 0); |
201 | 0 | while (pending.nr) { |
202 | 0 | result |= traverse_one_object(object_array_pop(&pending)); |
203 | 0 | display_progress(progress, ++nr); |
204 | 0 | } |
205 | 0 | stop_progress(&progress); |
206 | 0 | return !!result; |
207 | 0 | } |
208 | | |
209 | | static int mark_used(struct object *obj, enum object_type type UNUSED, |
210 | | void *data UNUSED, struct fsck_options *options UNUSED) |
211 | 0 | { |
212 | 0 | if (!obj) |
213 | 0 | return 1; |
214 | 0 | obj->flags |= USED; |
215 | 0 | return 0; |
216 | 0 | } |
217 | | |
218 | | static void mark_unreachable_referents(const struct object_id *oid) |
219 | 0 | { |
220 | 0 | struct fsck_options options = FSCK_OPTIONS_DEFAULT; |
221 | 0 | struct object *obj = lookup_object(the_repository, oid); |
222 | |
|
223 | 0 | if (!obj || !(obj->flags & HAS_OBJ)) |
224 | 0 | return; /* not part of our original set */ |
225 | 0 | if (obj->flags & REACHABLE) |
226 | 0 | return; /* reachable objects already traversed */ |
227 | | |
228 | | /* |
229 | | * Avoid passing OBJ_NONE to fsck_walk, which will parse the object |
230 | | * (and we want to avoid parsing blobs). |
231 | | */ |
232 | 0 | if (obj->type == OBJ_NONE) { |
233 | 0 | enum object_type type = oid_object_info(the_repository, |
234 | 0 | &obj->oid, NULL); |
235 | 0 | if (type > 0) |
236 | 0 | object_as_type(obj, type, 0); |
237 | 0 | } |
238 | |
|
239 | 0 | options.walk = mark_used; |
240 | 0 | fsck_walk(obj, NULL, &options); |
241 | 0 | if (obj->type == OBJ_TREE) |
242 | 0 | free_tree_buffer((struct tree *)obj); |
243 | 0 | } |
244 | | |
245 | | static int mark_loose_unreachable_referents(const struct object_id *oid, |
246 | | const char *path UNUSED, |
247 | | void *data UNUSED) |
248 | 0 | { |
249 | 0 | mark_unreachable_referents(oid); |
250 | 0 | return 0; |
251 | 0 | } |
252 | | |
253 | | static int mark_packed_unreachable_referents(const struct object_id *oid, |
254 | | struct packed_git *pack UNUSED, |
255 | | uint32_t pos UNUSED, |
256 | | void *data UNUSED) |
257 | 0 | { |
258 | 0 | mark_unreachable_referents(oid); |
259 | 0 | return 0; |
260 | 0 | } |
261 | | |
262 | | /* |
263 | | * Check a single reachable object |
264 | | */ |
265 | | static void check_reachable_object(struct object *obj) |
266 | 0 | { |
267 | | /* |
268 | | * We obviously want the object to be parsed, |
269 | | * except if it was in a pack-file and we didn't |
270 | | * do a full fsck |
271 | | */ |
272 | 0 | if (!(obj->flags & HAS_OBJ)) { |
273 | 0 | if (is_promisor_object(&obj->oid)) |
274 | 0 | return; |
275 | 0 | if (has_object_pack(&obj->oid)) |
276 | 0 | return; /* it is in pack - forget about it */ |
277 | 0 | printf_ln(_("missing %s %s"), |
278 | 0 | printable_type(&obj->oid, obj->type), |
279 | 0 | describe_object(&obj->oid)); |
280 | 0 | errors_found |= ERROR_REACHABLE; |
281 | 0 | return; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | /* |
286 | | * Check a single unreachable object |
287 | | */ |
288 | | static void check_unreachable_object(struct object *obj) |
289 | 0 | { |
290 | | /* |
291 | | * Missing unreachable object? Ignore it. It's not like |
292 | | * we miss it (since it can't be reached), nor do we want |
293 | | * to complain about it being unreachable (since it does |
294 | | * not exist). |
295 | | */ |
296 | 0 | if (!(obj->flags & HAS_OBJ)) |
297 | 0 | return; |
298 | | |
299 | | /* |
300 | | * Unreachable object that exists? Show it if asked to, |
301 | | * since this is something that is prunable. |
302 | | */ |
303 | 0 | if (show_unreachable) { |
304 | 0 | printf_ln(_("unreachable %s %s"), |
305 | 0 | printable_type(&obj->oid, obj->type), |
306 | 0 | describe_object(&obj->oid)); |
307 | 0 | return; |
308 | 0 | } |
309 | | |
310 | | /* |
311 | | * "!USED" means that nothing at all points to it, including |
312 | | * other unreachable objects. In other words, it's the "tip" |
313 | | * of some set of unreachable objects, usually a commit that |
314 | | * got dropped. |
315 | | * |
316 | | * Such starting points are more interesting than some random |
317 | | * set of unreachable objects, so we show them even if the user |
318 | | * hasn't asked for _all_ unreachable objects. If you have |
319 | | * deleted a branch by mistake, this is a prime candidate to |
320 | | * start looking at, for example. |
321 | | */ |
322 | 0 | if (!(obj->flags & USED)) { |
323 | 0 | if (show_dangling) |
324 | 0 | printf_ln(_("dangling %s %s"), |
325 | 0 | printable_type(&obj->oid, obj->type), |
326 | 0 | describe_object(&obj->oid)); |
327 | 0 | if (write_lost_and_found) { |
328 | 0 | char *filename = git_pathdup("lost-found/%s/%s", |
329 | 0 | obj->type == OBJ_COMMIT ? "commit" : "other", |
330 | 0 | describe_object(&obj->oid)); |
331 | 0 | FILE *f; |
332 | |
|
333 | 0 | if (safe_create_leading_directories_const(filename)) { |
334 | 0 | error(_("could not create lost-found")); |
335 | 0 | free(filename); |
336 | 0 | return; |
337 | 0 | } |
338 | 0 | f = xfopen(filename, "w"); |
339 | 0 | if (obj->type == OBJ_BLOB) { |
340 | 0 | if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1)) |
341 | 0 | die_errno(_("could not write '%s'"), filename); |
342 | 0 | } else |
343 | 0 | fprintf(f, "%s\n", describe_object(&obj->oid)); |
344 | 0 | if (fclose(f)) |
345 | 0 | die_errno(_("could not finish '%s'"), |
346 | 0 | filename); |
347 | 0 | free(filename); |
348 | 0 | } |
349 | 0 | return; |
350 | 0 | } |
351 | | |
352 | | /* |
353 | | * Otherwise? It's there, it's unreachable, and some other unreachable |
354 | | * object points to it. Ignore it - it's not interesting, and we showed |
355 | | * all the interesting cases above. |
356 | | */ |
357 | 0 | } |
358 | | |
359 | | static void check_object(struct object *obj) |
360 | 0 | { |
361 | 0 | if (verbose) |
362 | 0 | fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid)); |
363 | |
|
364 | 0 | if (obj->flags & REACHABLE) |
365 | 0 | check_reachable_object(obj); |
366 | 0 | else |
367 | 0 | check_unreachable_object(obj); |
368 | 0 | } |
369 | | |
370 | | static void check_connectivity(void) |
371 | 0 | { |
372 | 0 | int i, max; |
373 | | |
374 | | /* Traverse the pending reachable objects */ |
375 | 0 | traverse_reachable(); |
376 | | |
377 | | /* |
378 | | * With --connectivity-only, we won't have actually opened and marked |
379 | | * unreachable objects with USED. Do that now to make --dangling, etc |
380 | | * accurate. |
381 | | */ |
382 | 0 | if (connectivity_only && (show_dangling || write_lost_and_found)) { |
383 | | /* |
384 | | * Even though we already have a "struct object" for each of |
385 | | * these in memory, we must not iterate over the internal |
386 | | * object hash as we do below. Our loop would potentially |
387 | | * resize the hash, making our iteration invalid. |
388 | | * |
389 | | * Instead, we'll just go back to the source list of objects, |
390 | | * and ignore any that weren't present in our earlier |
391 | | * traversal. |
392 | | */ |
393 | 0 | for_each_loose_object(mark_loose_unreachable_referents, NULL, 0); |
394 | 0 | for_each_packed_object(mark_packed_unreachable_referents, NULL, 0); |
395 | 0 | } |
396 | | |
397 | | /* Look up all the requirements, warn about missing objects.. */ |
398 | 0 | max = get_max_object_index(); |
399 | 0 | if (verbose) |
400 | 0 | fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max); |
401 | |
|
402 | 0 | for (i = 0; i < max; i++) { |
403 | 0 | struct object *obj = get_indexed_object(i); |
404 | |
|
405 | 0 | if (obj) |
406 | 0 | check_object(obj); |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | | static int fsck_obj(struct object *obj, void *buffer, unsigned long size) |
411 | 0 | { |
412 | 0 | int err; |
413 | |
|
414 | 0 | if (obj->flags & SEEN) |
415 | 0 | return 0; |
416 | 0 | obj->flags |= SEEN; |
417 | |
|
418 | 0 | if (verbose) |
419 | 0 | fprintf_ln(stderr, _("Checking %s %s"), |
420 | 0 | printable_type(&obj->oid, obj->type), |
421 | 0 | describe_object(&obj->oid)); |
422 | |
|
423 | 0 | if (fsck_walk(obj, NULL, &fsck_obj_options)) |
424 | 0 | objerror(obj, _("broken links")); |
425 | 0 | err = fsck_object(obj, buffer, size, &fsck_obj_options); |
426 | 0 | if (err) |
427 | 0 | goto out; |
428 | | |
429 | 0 | if (obj->type == OBJ_COMMIT) { |
430 | 0 | struct commit *commit = (struct commit *) obj; |
431 | |
|
432 | 0 | if (!commit->parents && show_root) |
433 | 0 | printf_ln(_("root %s"), |
434 | 0 | describe_object(&commit->object.oid)); |
435 | 0 | } |
436 | |
|
437 | 0 | if (obj->type == OBJ_TAG) { |
438 | 0 | struct tag *tag = (struct tag *) obj; |
439 | |
|
440 | 0 | if (show_tags && tag->tagged) { |
441 | 0 | printf_ln(_("tagged %s %s (%s) in %s"), |
442 | 0 | printable_type(&tag->tagged->oid, tag->tagged->type), |
443 | 0 | describe_object(&tag->tagged->oid), |
444 | 0 | tag->tag, |
445 | 0 | describe_object(&tag->object.oid)); |
446 | 0 | } |
447 | 0 | } |
448 | |
|
449 | 0 | out: |
450 | 0 | if (obj->type == OBJ_TREE) |
451 | 0 | free_tree_buffer((struct tree *)obj); |
452 | 0 | return err; |
453 | 0 | } |
454 | | |
455 | | static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, |
456 | | unsigned long size, void *buffer, int *eaten) |
457 | 0 | { |
458 | | /* |
459 | | * Note, buffer may be NULL if type is OBJ_BLOB. See |
460 | | * verify_packfile(), data_valid variable for details. |
461 | | */ |
462 | 0 | struct object *obj; |
463 | 0 | obj = parse_object_buffer(the_repository, oid, type, size, buffer, |
464 | 0 | eaten); |
465 | 0 | if (!obj) { |
466 | 0 | errors_found |= ERROR_OBJECT; |
467 | 0 | return error(_("%s: object corrupt or missing"), |
468 | 0 | oid_to_hex(oid)); |
469 | 0 | } |
470 | 0 | obj->flags &= ~(REACHABLE | SEEN); |
471 | 0 | obj->flags |= HAS_OBJ; |
472 | 0 | return fsck_obj(obj, buffer, size); |
473 | 0 | } |
474 | | |
475 | | static int default_refs; |
476 | | |
477 | | static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, |
478 | | timestamp_t timestamp) |
479 | 0 | { |
480 | 0 | struct object *obj; |
481 | |
|
482 | 0 | if (!is_null_oid(oid)) { |
483 | 0 | obj = lookup_object(the_repository, oid); |
484 | 0 | if (obj && (obj->flags & HAS_OBJ)) { |
485 | 0 | if (timestamp) |
486 | 0 | fsck_put_object_name(&fsck_walk_options, oid, |
487 | 0 | "%s@{%"PRItime"}", |
488 | 0 | refname, timestamp); |
489 | 0 | obj->flags |= USED; |
490 | 0 | mark_object_reachable(obj); |
491 | 0 | } else if (!is_promisor_object(oid)) { |
492 | 0 | error(_("%s: invalid reflog entry %s"), |
493 | 0 | refname, oid_to_hex(oid)); |
494 | 0 | errors_found |= ERROR_REACHABLE; |
495 | 0 | } |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | | static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, |
500 | | const char *email UNUSED, |
501 | | timestamp_t timestamp, int tz UNUSED, |
502 | | const char *message UNUSED, void *cb_data) |
503 | 0 | { |
504 | 0 | const char *refname = cb_data; |
505 | |
|
506 | 0 | if (verbose) |
507 | 0 | fprintf_ln(stderr, _("Checking reflog %s->%s"), |
508 | 0 | oid_to_hex(ooid), oid_to_hex(noid)); |
509 | |
|
510 | 0 | fsck_handle_reflog_oid(refname, ooid, 0); |
511 | 0 | fsck_handle_reflog_oid(refname, noid, timestamp); |
512 | 0 | return 0; |
513 | 0 | } |
514 | | |
515 | | static int fsck_handle_reflog(const char *logname, void *cb_data) |
516 | 0 | { |
517 | 0 | struct strbuf refname = STRBUF_INIT; |
518 | |
|
519 | 0 | strbuf_worktree_ref(cb_data, &refname, logname); |
520 | 0 | refs_for_each_reflog_ent(get_main_ref_store(the_repository), |
521 | 0 | refname.buf, fsck_handle_reflog_ent, |
522 | 0 | refname.buf); |
523 | 0 | strbuf_release(&refname); |
524 | 0 | return 0; |
525 | 0 | } |
526 | | |
527 | | static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, |
528 | | int flag UNUSED, void *cb_data UNUSED) |
529 | 0 | { |
530 | 0 | struct object *obj; |
531 | |
|
532 | 0 | obj = parse_object(the_repository, oid); |
533 | 0 | if (!obj) { |
534 | 0 | if (is_promisor_object(oid)) { |
535 | | /* |
536 | | * Increment default_refs anyway, because this is a |
537 | | * valid ref. |
538 | | */ |
539 | 0 | default_refs++; |
540 | 0 | return 0; |
541 | 0 | } |
542 | 0 | error(_("%s: invalid sha1 pointer %s"), |
543 | 0 | refname, oid_to_hex(oid)); |
544 | 0 | errors_found |= ERROR_REACHABLE; |
545 | | /* We'll continue with the rest despite the error.. */ |
546 | 0 | return 0; |
547 | 0 | } |
548 | 0 | if (obj->type != OBJ_COMMIT && is_branch(refname)) { |
549 | 0 | error(_("%s: not a commit"), refname); |
550 | 0 | errors_found |= ERROR_REFS; |
551 | 0 | } |
552 | 0 | default_refs++; |
553 | 0 | obj->flags |= USED; |
554 | 0 | fsck_put_object_name(&fsck_walk_options, |
555 | 0 | oid, "%s", refname); |
556 | 0 | mark_object_reachable(obj); |
557 | |
|
558 | 0 | return 0; |
559 | 0 | } |
560 | | |
561 | | static int fsck_head_link(const char *head_ref_name, |
562 | | const char **head_points_at, |
563 | | struct object_id *head_oid); |
564 | | |
565 | | static void get_default_heads(void) |
566 | 0 | { |
567 | 0 | struct worktree **worktrees, **p; |
568 | 0 | const char *head_points_at; |
569 | 0 | struct object_id head_oid; |
570 | |
|
571 | 0 | refs_for_each_rawref(get_main_ref_store(the_repository), |
572 | 0 | fsck_handle_ref, NULL); |
573 | |
|
574 | 0 | worktrees = get_worktrees(); |
575 | 0 | for (p = worktrees; *p; p++) { |
576 | 0 | struct worktree *wt = *p; |
577 | 0 | struct strbuf ref = STRBUF_INIT; |
578 | |
|
579 | 0 | strbuf_worktree_ref(wt, &ref, "HEAD"); |
580 | 0 | fsck_head_link(ref.buf, &head_points_at, &head_oid); |
581 | 0 | if (head_points_at && !is_null_oid(&head_oid)) |
582 | 0 | fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL); |
583 | 0 | strbuf_release(&ref); |
584 | |
|
585 | 0 | if (include_reflogs) |
586 | 0 | refs_for_each_reflog(get_worktree_ref_store(wt), |
587 | 0 | fsck_handle_reflog, wt); |
588 | 0 | } |
589 | 0 | free_worktrees(worktrees); |
590 | | |
591 | | /* |
592 | | * Not having any default heads isn't really fatal, but |
593 | | * it does mean that "--unreachable" no longer makes any |
594 | | * sense (since in this case everything will obviously |
595 | | * be unreachable by definition. |
596 | | * |
597 | | * Showing dangling objects is valid, though (as those |
598 | | * dangling objects are likely lost heads). |
599 | | * |
600 | | * So we just print a warning about it, and clear the |
601 | | * "show_unreachable" flag. |
602 | | */ |
603 | 0 | if (!default_refs) { |
604 | 0 | fprintf_ln(stderr, _("notice: No default references")); |
605 | 0 | show_unreachable = 0; |
606 | 0 | } |
607 | 0 | } |
608 | | |
609 | | struct for_each_loose_cb |
610 | | { |
611 | | struct progress *progress; |
612 | | struct strbuf obj_type; |
613 | | }; |
614 | | |
615 | | static int fsck_loose(const struct object_id *oid, const char *path, void *data) |
616 | 0 | { |
617 | 0 | struct for_each_loose_cb *cb_data = data; |
618 | 0 | struct object *obj; |
619 | 0 | enum object_type type = OBJ_NONE; |
620 | 0 | unsigned long size; |
621 | 0 | void *contents = NULL; |
622 | 0 | int eaten; |
623 | 0 | struct object_info oi = OBJECT_INFO_INIT; |
624 | 0 | struct object_id real_oid = *null_oid(); |
625 | 0 | int err = 0; |
626 | |
|
627 | 0 | strbuf_reset(&cb_data->obj_type); |
628 | 0 | oi.type_name = &cb_data->obj_type; |
629 | 0 | oi.sizep = &size; |
630 | 0 | oi.typep = &type; |
631 | |
|
632 | 0 | if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) { |
633 | 0 | if (contents && !oideq(&real_oid, oid)) |
634 | 0 | err = error(_("%s: hash-path mismatch, found at: %s"), |
635 | 0 | oid_to_hex(&real_oid), path); |
636 | 0 | else |
637 | 0 | err = error(_("%s: object corrupt or missing: %s"), |
638 | 0 | oid_to_hex(oid), path); |
639 | 0 | } |
640 | 0 | if (type != OBJ_NONE && type < 0) |
641 | 0 | err = error(_("%s: object is of unknown type '%s': %s"), |
642 | 0 | oid_to_hex(&real_oid), cb_data->obj_type.buf, |
643 | 0 | path); |
644 | 0 | if (err < 0) { |
645 | 0 | errors_found |= ERROR_OBJECT; |
646 | 0 | free(contents); |
647 | 0 | return 0; /* keep checking other objects */ |
648 | 0 | } |
649 | | |
650 | 0 | if (!contents && type != OBJ_BLOB) |
651 | 0 | BUG("read_loose_object streamed a non-blob"); |
652 | | |
653 | 0 | obj = parse_object_buffer(the_repository, oid, type, size, |
654 | 0 | contents, &eaten); |
655 | |
|
656 | 0 | if (!obj) { |
657 | 0 | errors_found |= ERROR_OBJECT; |
658 | 0 | error(_("%s: object could not be parsed: %s"), |
659 | 0 | oid_to_hex(oid), path); |
660 | 0 | if (!eaten) |
661 | 0 | free(contents); |
662 | 0 | return 0; /* keep checking other objects */ |
663 | 0 | } |
664 | | |
665 | 0 | obj->flags &= ~(REACHABLE | SEEN); |
666 | 0 | obj->flags |= HAS_OBJ; |
667 | 0 | if (fsck_obj(obj, contents, size)) |
668 | 0 | errors_found |= ERROR_OBJECT; |
669 | |
|
670 | 0 | if (!eaten) |
671 | 0 | free(contents); |
672 | 0 | return 0; /* keep checking other objects, even if we saw an error */ |
673 | 0 | } |
674 | | |
675 | | static int fsck_cruft(const char *basename, const char *path, |
676 | | void *data UNUSED) |
677 | 0 | { |
678 | 0 | if (!starts_with(basename, "tmp_obj_")) |
679 | 0 | fprintf_ln(stderr, _("bad sha1 file: %s"), path); |
680 | 0 | return 0; |
681 | 0 | } |
682 | | |
683 | | static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) |
684 | 0 | { |
685 | 0 | struct for_each_loose_cb *cb_data = data; |
686 | 0 | struct progress *progress = cb_data->progress; |
687 | 0 | display_progress(progress, nr + 1); |
688 | 0 | return 0; |
689 | 0 | } |
690 | | |
691 | | static void fsck_object_dir(const char *path) |
692 | 0 | { |
693 | 0 | struct progress *progress = NULL; |
694 | 0 | struct for_each_loose_cb cb_data = { |
695 | 0 | .obj_type = STRBUF_INIT, |
696 | 0 | .progress = progress, |
697 | 0 | }; |
698 | |
|
699 | 0 | if (verbose) |
700 | 0 | fprintf_ln(stderr, _("Checking object directory")); |
701 | |
|
702 | 0 | if (show_progress) |
703 | 0 | progress = start_progress(_("Checking object directories"), 256); |
704 | |
|
705 | 0 | for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir, |
706 | 0 | &cb_data); |
707 | 0 | display_progress(progress, 256); |
708 | 0 | stop_progress(&progress); |
709 | 0 | strbuf_release(&cb_data.obj_type); |
710 | 0 | } |
711 | | |
712 | | static int fsck_head_link(const char *head_ref_name, |
713 | | const char **head_points_at, |
714 | | struct object_id *head_oid) |
715 | 0 | { |
716 | 0 | int null_is_error = 0; |
717 | |
|
718 | 0 | if (verbose) |
719 | 0 | fprintf_ln(stderr, _("Checking %s link"), head_ref_name); |
720 | |
|
721 | 0 | *head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), |
722 | 0 | head_ref_name, 0, head_oid, |
723 | 0 | NULL); |
724 | 0 | if (!*head_points_at) { |
725 | 0 | errors_found |= ERROR_REFS; |
726 | 0 | return error(_("invalid %s"), head_ref_name); |
727 | 0 | } |
728 | 0 | if (!strcmp(*head_points_at, head_ref_name)) |
729 | | /* detached HEAD */ |
730 | 0 | null_is_error = 1; |
731 | 0 | else if (!starts_with(*head_points_at, "refs/heads/")) { |
732 | 0 | errors_found |= ERROR_REFS; |
733 | 0 | return error(_("%s points to something strange (%s)"), |
734 | 0 | head_ref_name, *head_points_at); |
735 | 0 | } |
736 | 0 | if (is_null_oid(head_oid)) { |
737 | 0 | if (null_is_error) { |
738 | 0 | errors_found |= ERROR_REFS; |
739 | 0 | return error(_("%s: detached HEAD points at nothing"), |
740 | 0 | head_ref_name); |
741 | 0 | } |
742 | 0 | fprintf_ln(stderr, |
743 | 0 | _("notice: %s points to an unborn branch (%s)"), |
744 | 0 | head_ref_name, *head_points_at + 11); |
745 | 0 | } |
746 | 0 | return 0; |
747 | 0 | } |
748 | | |
749 | | static int fsck_cache_tree(struct cache_tree *it, const char *index_path) |
750 | 0 | { |
751 | 0 | int i; |
752 | 0 | int err = 0; |
753 | |
|
754 | 0 | if (verbose) |
755 | 0 | fprintf_ln(stderr, _("Checking cache tree of %s"), index_path); |
756 | |
|
757 | 0 | if (0 <= it->entry_count) { |
758 | 0 | struct object *obj = parse_object(the_repository, &it->oid); |
759 | 0 | if (!obj) { |
760 | 0 | error(_("%s: invalid sha1 pointer in cache-tree of %s"), |
761 | 0 | oid_to_hex(&it->oid), index_path); |
762 | 0 | errors_found |= ERROR_REFS; |
763 | 0 | return 1; |
764 | 0 | } |
765 | 0 | obj->flags |= USED; |
766 | 0 | fsck_put_object_name(&fsck_walk_options, &it->oid, ":"); |
767 | 0 | mark_object_reachable(obj); |
768 | 0 | if (obj->type != OBJ_TREE) |
769 | 0 | err |= objerror(obj, _("non-tree in cache-tree")); |
770 | 0 | } |
771 | 0 | for (i = 0; i < it->subtree_nr; i++) |
772 | 0 | err |= fsck_cache_tree(it->down[i]->cache_tree, index_path); |
773 | 0 | return err; |
774 | 0 | } |
775 | | |
776 | | static int fsck_resolve_undo(struct index_state *istate, |
777 | | const char *index_path) |
778 | 0 | { |
779 | 0 | struct string_list_item *item; |
780 | 0 | struct string_list *resolve_undo = istate->resolve_undo; |
781 | |
|
782 | 0 | if (!resolve_undo) |
783 | 0 | return 0; |
784 | | |
785 | 0 | for_each_string_list_item(item, resolve_undo) { |
786 | 0 | const char *path = item->string; |
787 | 0 | struct resolve_undo_info *ru = item->util; |
788 | 0 | int i; |
789 | |
|
790 | 0 | if (!ru) |
791 | 0 | continue; |
792 | 0 | for (i = 0; i < 3; i++) { |
793 | 0 | struct object *obj; |
794 | |
|
795 | 0 | if (!ru->mode[i] || !S_ISREG(ru->mode[i])) |
796 | 0 | continue; |
797 | | |
798 | 0 | obj = parse_object(the_repository, &ru->oid[i]); |
799 | 0 | if (!obj) { |
800 | 0 | error(_("%s: invalid sha1 pointer in resolve-undo of %s"), |
801 | 0 | oid_to_hex(&ru->oid[i]), |
802 | 0 | index_path); |
803 | 0 | errors_found |= ERROR_REFS; |
804 | 0 | continue; |
805 | 0 | } |
806 | 0 | obj->flags |= USED; |
807 | 0 | fsck_put_object_name(&fsck_walk_options, &ru->oid[i], |
808 | 0 | ":(%d):%s", i, path); |
809 | 0 | mark_object_reachable(obj); |
810 | 0 | } |
811 | 0 | } |
812 | 0 | return 0; |
813 | 0 | } |
814 | | |
815 | | static void fsck_index(struct index_state *istate, const char *index_path, |
816 | | int is_current_worktree) |
817 | 0 | { |
818 | 0 | unsigned int i; |
819 | | |
820 | | /* TODO: audit for interaction with sparse-index. */ |
821 | 0 | ensure_full_index(istate); |
822 | 0 | for (i = 0; i < istate->cache_nr; i++) { |
823 | 0 | unsigned int mode; |
824 | 0 | struct blob *blob; |
825 | 0 | struct object *obj; |
826 | |
|
827 | 0 | mode = istate->cache[i]->ce_mode; |
828 | 0 | if (S_ISGITLINK(mode)) |
829 | 0 | continue; |
830 | 0 | blob = lookup_blob(the_repository, |
831 | 0 | &istate->cache[i]->oid); |
832 | 0 | if (!blob) |
833 | 0 | continue; |
834 | 0 | obj = &blob->object; |
835 | 0 | obj->flags |= USED; |
836 | 0 | fsck_put_object_name(&fsck_walk_options, &obj->oid, |
837 | 0 | "%s:%s", |
838 | 0 | is_current_worktree ? "" : index_path, |
839 | 0 | istate->cache[i]->name); |
840 | 0 | mark_object_reachable(obj); |
841 | 0 | } |
842 | 0 | if (istate->cache_tree) |
843 | 0 | fsck_cache_tree(istate->cache_tree, index_path); |
844 | 0 | fsck_resolve_undo(istate, index_path); |
845 | 0 | } |
846 | | |
847 | | static void mark_object_for_connectivity(const struct object_id *oid) |
848 | 0 | { |
849 | 0 | struct object *obj = lookup_unknown_object(the_repository, oid); |
850 | 0 | obj->flags |= HAS_OBJ; |
851 | 0 | } |
852 | | |
853 | | static int mark_loose_for_connectivity(const struct object_id *oid, |
854 | | const char *path UNUSED, |
855 | | void *data UNUSED) |
856 | 0 | { |
857 | 0 | mark_object_for_connectivity(oid); |
858 | 0 | return 0; |
859 | 0 | } |
860 | | |
861 | | static int mark_packed_for_connectivity(const struct object_id *oid, |
862 | | struct packed_git *pack UNUSED, |
863 | | uint32_t pos UNUSED, |
864 | | void *data UNUSED) |
865 | 0 | { |
866 | 0 | mark_object_for_connectivity(oid); |
867 | 0 | return 0; |
868 | 0 | } |
869 | | |
870 | | static int check_pack_rev_indexes(struct repository *r, int show_progress) |
871 | 0 | { |
872 | 0 | struct progress *progress = NULL; |
873 | 0 | uint32_t pack_count = 0; |
874 | 0 | int res = 0; |
875 | |
|
876 | 0 | if (show_progress) { |
877 | 0 | for (struct packed_git *p = get_all_packs(r); p; p = p->next) |
878 | 0 | pack_count++; |
879 | 0 | progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count); |
880 | 0 | pack_count = 0; |
881 | 0 | } |
882 | |
|
883 | 0 | for (struct packed_git *p = get_all_packs(r); p; p = p->next) { |
884 | 0 | int load_error = load_pack_revindex_from_disk(p); |
885 | |
|
886 | 0 | if (load_error < 0) { |
887 | 0 | error(_("unable to load rev-index for pack '%s'"), p->pack_name); |
888 | 0 | res = ERROR_PACK_REV_INDEX; |
889 | 0 | } else if (!load_error && |
890 | 0 | !load_pack_revindex(r, p) && |
891 | 0 | verify_pack_revindex(p)) { |
892 | 0 | error(_("invalid rev-index for pack '%s'"), p->pack_name); |
893 | 0 | res = ERROR_PACK_REV_INDEX; |
894 | 0 | } |
895 | 0 | display_progress(progress, ++pack_count); |
896 | 0 | } |
897 | 0 | stop_progress(&progress); |
898 | |
|
899 | 0 | return res; |
900 | 0 | } |
901 | | |
902 | | static char const * const fsck_usage[] = { |
903 | | N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" |
904 | | " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" |
905 | | " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" |
906 | | " [--[no-]name-objects] [<object>...]"), |
907 | | NULL |
908 | | }; |
909 | | |
910 | | static struct option fsck_opts[] = { |
911 | | OPT__VERBOSE(&verbose, N_("be verbose")), |
912 | | OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")), |
913 | | OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")), |
914 | | OPT_BOOL(0, "tags", &show_tags, N_("report tags")), |
915 | | OPT_BOOL(0, "root", &show_root, N_("report root nodes")), |
916 | | OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), |
917 | | OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), |
918 | | OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")), |
919 | | OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")), |
920 | | OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")), |
921 | | OPT_BOOL(0, "lost-found", &write_lost_and_found, |
922 | | N_("write dangling objects in .git/lost-found")), |
923 | | OPT_BOOL(0, "progress", &show_progress, N_("show progress")), |
924 | | OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")), |
925 | | OPT_END(), |
926 | | }; |
927 | | |
928 | | int cmd_fsck(int argc, const char **argv, const char *prefix) |
929 | 0 | { |
930 | 0 | int i; |
931 | 0 | struct object_directory *odb; |
932 | | |
933 | | /* fsck knows how to handle missing promisor objects */ |
934 | 0 | fetch_if_missing = 0; |
935 | |
|
936 | 0 | errors_found = 0; |
937 | 0 | disable_replace_refs(); |
938 | 0 | save_commit_buffer = 0; |
939 | |
|
940 | 0 | argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); |
941 | |
|
942 | 0 | fsck_walk_options.walk = mark_object; |
943 | 0 | fsck_obj_options.walk = mark_used; |
944 | 0 | fsck_obj_options.error_func = fsck_objects_error_func; |
945 | 0 | if (check_strict) |
946 | 0 | fsck_obj_options.strict = 1; |
947 | |
|
948 | 0 | if (show_progress == -1) |
949 | 0 | show_progress = isatty(2); |
950 | 0 | if (verbose) |
951 | 0 | show_progress = 0; |
952 | |
|
953 | 0 | if (write_lost_and_found) { |
954 | 0 | check_full = 1; |
955 | 0 | include_reflogs = 0; |
956 | 0 | } |
957 | |
|
958 | 0 | if (name_objects) |
959 | 0 | fsck_enable_object_names(&fsck_walk_options); |
960 | |
|
961 | 0 | git_config(git_fsck_config, &fsck_obj_options); |
962 | 0 | prepare_repo_settings(the_repository); |
963 | |
|
964 | 0 | if (connectivity_only) { |
965 | 0 | for_each_loose_object(mark_loose_for_connectivity, NULL, 0); |
966 | 0 | for_each_packed_object(mark_packed_for_connectivity, NULL, 0); |
967 | 0 | } else { |
968 | 0 | prepare_alt_odb(the_repository); |
969 | 0 | for (odb = the_repository->objects->odb; odb; odb = odb->next) |
970 | 0 | fsck_object_dir(odb->path); |
971 | |
|
972 | 0 | if (check_full) { |
973 | 0 | struct packed_git *p; |
974 | 0 | uint32_t total = 0, count = 0; |
975 | 0 | struct progress *progress = NULL; |
976 | |
|
977 | 0 | if (show_progress) { |
978 | 0 | for (p = get_all_packs(the_repository); p; |
979 | 0 | p = p->next) { |
980 | 0 | if (open_pack_index(p)) |
981 | 0 | continue; |
982 | 0 | total += p->num_objects; |
983 | 0 | } |
984 | |
|
985 | 0 | progress = start_progress(_("Checking objects"), total); |
986 | 0 | } |
987 | 0 | for (p = get_all_packs(the_repository); p; |
988 | 0 | p = p->next) { |
989 | | /* verify gives error messages itself */ |
990 | 0 | if (verify_pack(the_repository, |
991 | 0 | p, fsck_obj_buffer, |
992 | 0 | progress, count)) |
993 | 0 | errors_found |= ERROR_PACK; |
994 | 0 | count += p->num_objects; |
995 | 0 | } |
996 | 0 | stop_progress(&progress); |
997 | 0 | } |
998 | |
|
999 | 0 | if (fsck_finish(&fsck_obj_options)) |
1000 | 0 | errors_found |= ERROR_OBJECT; |
1001 | 0 | } |
1002 | |
|
1003 | 0 | for (i = 0; i < argc; i++) { |
1004 | 0 | const char *arg = argv[i]; |
1005 | 0 | struct object_id oid; |
1006 | 0 | if (!repo_get_oid(the_repository, arg, &oid)) { |
1007 | 0 | struct object *obj = lookup_object(the_repository, |
1008 | 0 | &oid); |
1009 | |
|
1010 | 0 | if (!obj || !(obj->flags & HAS_OBJ)) { |
1011 | 0 | if (is_promisor_object(&oid)) |
1012 | 0 | continue; |
1013 | 0 | error(_("%s: object missing"), oid_to_hex(&oid)); |
1014 | 0 | errors_found |= ERROR_OBJECT; |
1015 | 0 | continue; |
1016 | 0 | } |
1017 | | |
1018 | 0 | obj->flags |= USED; |
1019 | 0 | fsck_put_object_name(&fsck_walk_options, &oid, |
1020 | 0 | "%s", arg); |
1021 | 0 | mark_object_reachable(obj); |
1022 | 0 | continue; |
1023 | 0 | } |
1024 | 0 | error(_("invalid parameter: expected sha1, got '%s'"), arg); |
1025 | 0 | errors_found |= ERROR_OBJECT; |
1026 | 0 | } |
1027 | | |
1028 | | /* |
1029 | | * If we've not been given any explicit head information, do the |
1030 | | * default ones from .git/refs. We also consider the index file |
1031 | | * in this case (ie this implies --cache). |
1032 | | */ |
1033 | 0 | if (!argc) { |
1034 | 0 | get_default_heads(); |
1035 | 0 | keep_cache_objects = 1; |
1036 | 0 | } |
1037 | |
|
1038 | 0 | if (keep_cache_objects) { |
1039 | 0 | struct worktree **worktrees, **p; |
1040 | |
|
1041 | 0 | verify_index_checksum = 1; |
1042 | 0 | verify_ce_order = 1; |
1043 | |
|
1044 | 0 | worktrees = get_worktrees(); |
1045 | 0 | for (p = worktrees; *p; p++) { |
1046 | 0 | struct worktree *wt = *p; |
1047 | 0 | struct index_state istate = |
1048 | 0 | INDEX_STATE_INIT(the_repository); |
1049 | 0 | char *path; |
1050 | | |
1051 | | /* |
1052 | | * Make a copy since the buffer is reusable |
1053 | | * and may get overwritten by other calls |
1054 | | * while we're examining the index. |
1055 | | */ |
1056 | 0 | path = xstrdup(worktree_git_path(the_repository, wt, "index")); |
1057 | 0 | read_index_from(&istate, path, get_worktree_git_dir(wt)); |
1058 | 0 | fsck_index(&istate, path, wt->is_current); |
1059 | 0 | discard_index(&istate); |
1060 | 0 | free(path); |
1061 | 0 | } |
1062 | 0 | free_worktrees(worktrees); |
1063 | 0 | } |
1064 | |
|
1065 | 0 | errors_found |= check_pack_rev_indexes(the_repository, show_progress); |
1066 | 0 | if (verify_bitmap_files(the_repository)) |
1067 | 0 | errors_found |= ERROR_BITMAP; |
1068 | |
|
1069 | 0 | check_connectivity(); |
1070 | |
|
1071 | 0 | if (the_repository->settings.core_commit_graph) { |
1072 | 0 | struct child_process commit_graph_verify = CHILD_PROCESS_INIT; |
1073 | |
|
1074 | 0 | prepare_alt_odb(the_repository); |
1075 | 0 | for (odb = the_repository->objects->odb; odb; odb = odb->next) { |
1076 | 0 | child_process_init(&commit_graph_verify); |
1077 | 0 | commit_graph_verify.git_cmd = 1; |
1078 | 0 | strvec_pushl(&commit_graph_verify.args, "commit-graph", |
1079 | 0 | "verify", "--object-dir", odb->path, NULL); |
1080 | 0 | if (show_progress) |
1081 | 0 | strvec_push(&commit_graph_verify.args, "--progress"); |
1082 | 0 | else |
1083 | 0 | strvec_push(&commit_graph_verify.args, "--no-progress"); |
1084 | 0 | if (run_command(&commit_graph_verify)) |
1085 | 0 | errors_found |= ERROR_COMMIT_GRAPH; |
1086 | 0 | } |
1087 | 0 | } |
1088 | |
|
1089 | 0 | if (the_repository->settings.core_multi_pack_index) { |
1090 | 0 | struct child_process midx_verify = CHILD_PROCESS_INIT; |
1091 | |
|
1092 | 0 | prepare_alt_odb(the_repository); |
1093 | 0 | for (odb = the_repository->objects->odb; odb; odb = odb->next) { |
1094 | 0 | child_process_init(&midx_verify); |
1095 | 0 | midx_verify.git_cmd = 1; |
1096 | 0 | strvec_pushl(&midx_verify.args, "multi-pack-index", |
1097 | 0 | "verify", "--object-dir", odb->path, NULL); |
1098 | 0 | if (show_progress) |
1099 | 0 | strvec_push(&midx_verify.args, "--progress"); |
1100 | 0 | else |
1101 | 0 | strvec_push(&midx_verify.args, "--no-progress"); |
1102 | 0 | if (run_command(&midx_verify)) |
1103 | 0 | errors_found |= ERROR_MULTI_PACK_INDEX; |
1104 | 0 | } |
1105 | 0 | } |
1106 | |
|
1107 | 0 | return errors_found; |
1108 | 0 | } |