Git fork

Merge branch 'sj/ref-fsck'

"git fsck" infrastructure has been taught to also check the sanity
of the ref database, in addition to the object database.

* sj/ref-fsck:
fsck: add ref name check for files backend
files-backend: add unified interface for refs scanning
builtin/refs: add verify subcommand
refs: set up ref consistency check infrastructure
fsck: add refs report function
fsck: add a unified interface for reporting fsck messages
fsck: make "fsck_error" callback generic
fsck: rename objects-related fsck error functions
fsck: rename "skiplist" to "skip_oids"

+477 -59
+6
Documentation/fsck-msgids.txt
··· 19 19 `badParentSha1`:: 20 20 (ERROR) A commit object has a bad parent sha1. 21 21 22 + `badRefFiletype`:: 23 + (ERROR) A ref has a bad file type. 24 + 25 + `badRefName`:: 26 + (ERROR) A ref has an invalid format. 27 + 22 28 `badTagName`:: 23 29 (INFO) A tag has an invalid format. 24 30
+13
Documentation/git-refs.txt
··· 10 10 -------- 11 11 [verse] 12 12 'git refs migrate' --ref-format=<format> [--dry-run] 13 + 'git refs verify' [--strict] [--verbose] 13 14 14 15 DESCRIPTION 15 16 ----------- ··· 21 22 22 23 migrate:: 23 24 Migrate ref store between different formats. 25 + 26 + verify:: 27 + Verify reference database consistency. 24 28 25 29 OPTIONS 26 30 ------- ··· 38 42 separately. The name of the directory will be reported on stdout. This 39 43 can be used to double check that the migration works as expected before 40 44 performing the actual migration. 45 + 46 + The following options are specific to 'git refs verify': 47 + 48 + --strict:: 49 + Enable stricter error checking. This will cause warnings to be 50 + reported as errors. See linkgit:git-fsck[1]. 51 + 52 + --verbose:: 53 + When verifying the reference database consistency, be chatty. 41 54 42 55 KNOWN LIMITATIONS 43 56 -----------------
+10 -7
builtin/fsck.c
··· 89 89 return -1; 90 90 } 91 91 92 - static int fsck_error_func(struct fsck_options *o UNUSED, 93 - const struct object_id *oid, 94 - enum object_type object_type, 95 - enum fsck_msg_type msg_type, 96 - enum fsck_msg_id msg_id UNUSED, 97 - const char *message) 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) 98 97 { 98 + struct fsck_object_report *report = fsck_report; 99 + const struct object_id *oid = report->oid; 100 + enum object_type object_type = report->object_type; 101 + 99 102 switch (msg_type) { 100 103 case FSCK_WARN: 101 104 /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */ ··· 938 941 939 942 fsck_walk_options.walk = mark_object; 940 943 fsck_obj_options.walk = mark_used; 941 - fsck_obj_options.error_func = fsck_error_func; 944 + fsck_obj_options.error_func = fsck_objects_error_func; 942 945 if (check_strict) 943 946 fsck_obj_options.strict = 1; 944 947
+1 -2
builtin/mktag.c
··· 18 18 static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; 19 19 20 20 static int mktag_fsck_error_func(struct fsck_options *o UNUSED, 21 - const struct object_id *oid UNUSED, 22 - enum object_type object_type UNUSED, 21 + void *fsck_report UNUSED, 23 22 enum fsck_msg_type msg_type, 24 23 enum fsck_msg_id msg_id UNUSED, 25 24 const char *message)
+34
builtin/refs.c
··· 1 1 #include "builtin.h" 2 + #include "config.h" 3 + #include "fsck.h" 2 4 #include "parse-options.h" 3 5 #include "refs.h" 4 6 #include "repository.h" ··· 6 8 7 9 #define REFS_MIGRATE_USAGE \ 8 10 N_("git refs migrate --ref-format=<format> [--dry-run]") 11 + 12 + #define REFS_VERIFY_USAGE \ 13 + N_("git refs verify [--strict] [--verbose]") 9 14 10 15 static int cmd_refs_migrate(int argc, const char **argv, const char *prefix) 11 16 { ··· 58 63 return err; 59 64 } 60 65 66 + static int cmd_refs_verify(int argc, const char **argv, const char *prefix) 67 + { 68 + struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT; 69 + const char * const verify_usage[] = { 70 + REFS_VERIFY_USAGE, 71 + NULL, 72 + }; 73 + struct option options[] = { 74 + OPT_BOOL(0, "verbose", &fsck_refs_options.verbose, N_("be verbose")), 75 + OPT_BOOL(0, "strict", &fsck_refs_options.strict, N_("enable strict checking")), 76 + OPT_END(), 77 + }; 78 + int ret; 79 + 80 + argc = parse_options(argc, argv, prefix, options, verify_usage, 0); 81 + if (argc) 82 + usage(_("'git refs verify' takes no arguments")); 83 + 84 + git_config(git_fsck_config, &fsck_refs_options); 85 + prepare_repo_settings(the_repository); 86 + 87 + ret = refs_fsck(get_main_ref_store(the_repository), &fsck_refs_options); 88 + 89 + fsck_options_clear(&fsck_refs_options); 90 + return ret; 91 + } 92 + 61 93 int cmd_refs(int argc, const char **argv, const char *prefix) 62 94 { 63 95 const char * const refs_usage[] = { 64 96 REFS_MIGRATE_USAGE, 97 + REFS_VERIFY_USAGE, 65 98 NULL, 66 99 }; 67 100 parse_opt_subcommand_fn *fn = NULL; 68 101 struct option opts[] = { 69 102 OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate), 103 + OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify), 70 104 OPT_END(), 71 105 }; 72 106
+99 -26
fsck.c
··· 205 205 if (!strcmp(buf, "skiplist")) { 206 206 if (equal == len) 207 207 die("skiplist requires a path"); 208 - oidset_parse_file(&options->skiplist, buf + equal + 1, 208 + oidset_parse_file(&options->skip_oids, buf + equal + 1, 209 209 the_repository->hash_algo); 210 210 buf += len + 1; 211 211 continue; ··· 223 223 static int object_on_skiplist(struct fsck_options *opts, 224 224 const struct object_id *oid) 225 225 { 226 - return opts && oid && oidset_contains(&opts->skiplist, oid); 226 + return opts && oid && oidset_contains(&opts->skip_oids, oid); 227 227 } 228 228 229 - __attribute__((format (printf, 5, 6))) 230 - static int report(struct fsck_options *options, 231 - const struct object_id *oid, enum object_type object_type, 232 - enum fsck_msg_id msg_id, const char *fmt, ...) 229 + /* 230 + * Provide the common functionality for either fscking refs or objects. 231 + * It will get the current msg error type and call the error_func callback 232 + * which is registered in the "fsck_options" struct. 233 + */ 234 + static int fsck_vreport(struct fsck_options *options, 235 + void *fsck_report, 236 + enum fsck_msg_id msg_id, const char *fmt, va_list ap) 233 237 { 234 - va_list ap; 235 238 struct strbuf sb = STRBUF_INIT; 236 239 enum fsck_msg_type msg_type = fsck_msg_type(msg_id, options); 237 240 int result; ··· 239 242 if (msg_type == FSCK_IGNORE) 240 243 return 0; 241 244 242 - if (object_on_skiplist(options, oid)) 243 - return 0; 244 - 245 245 if (msg_type == FSCK_FATAL) 246 246 msg_type = FSCK_ERROR; 247 247 else if (msg_type == FSCK_INFO) ··· 250 250 prepare_msg_ids(); 251 251 strbuf_addf(&sb, "%s: ", msg_id_info[msg_id].camelcased); 252 252 253 - va_start(ap, fmt); 254 253 strbuf_vaddf(&sb, fmt, ap); 255 - result = options->error_func(options, oid, object_type, 254 + result = options->error_func(options, fsck_report, 256 255 msg_type, msg_id, sb.buf); 257 256 strbuf_release(&sb); 257 + 258 + return result; 259 + } 260 + 261 + __attribute__((format (printf, 5, 6))) 262 + static int report(struct fsck_options *options, 263 + const struct object_id *oid, enum object_type object_type, 264 + enum fsck_msg_id msg_id, const char *fmt, ...) 265 + { 266 + va_list ap; 267 + struct fsck_object_report report = { 268 + .oid = oid, 269 + .object_type = object_type 270 + }; 271 + int result; 272 + 273 + if (object_on_skiplist(options, oid)) 274 + return 0; 275 + 276 + va_start(ap, fmt); 277 + result = fsck_vreport(options, &report, msg_id, fmt, ap); 258 278 va_end(ap); 259 279 280 + return result; 281 + } 282 + 283 + int fsck_report_ref(struct fsck_options *options, 284 + struct fsck_ref_report *report, 285 + enum fsck_msg_id msg_id, 286 + const char *fmt, ...) 287 + { 288 + va_list ap; 289 + int result; 290 + va_start(ap, fmt); 291 + result = fsck_vreport(options, report, msg_id, fmt, ap); 292 + va_end(ap); 260 293 return result; 261 294 } 262 295 ··· 1200 1233 type); 1201 1234 } 1202 1235 1203 - int fsck_error_function(struct fsck_options *o, 1204 - const struct object_id *oid, 1205 - enum object_type object_type UNUSED, 1206 - enum fsck_msg_type msg_type, 1207 - enum fsck_msg_id msg_id UNUSED, 1208 - const char *message) 1236 + int fsck_objects_error_function(struct fsck_options *o, 1237 + void *fsck_report, 1238 + enum fsck_msg_type msg_type, 1239 + enum fsck_msg_id msg_id UNUSED, 1240 + const char *message) 1209 1241 { 1242 + struct fsck_object_report *report = fsck_report; 1243 + const struct object_id *oid = report->oid; 1244 + 1210 1245 if (msg_type == FSCK_WARN) { 1211 1246 warning("object %s: %s", fsck_describe_object(o, oid), message); 1212 1247 return 0; ··· 1215 1250 return 1; 1216 1251 } 1217 1252 1253 + int fsck_refs_error_function(struct fsck_options *options UNUSED, 1254 + void *fsck_report, 1255 + enum fsck_msg_type msg_type, 1256 + enum fsck_msg_id msg_id UNUSED, 1257 + const char *message) 1258 + { 1259 + struct fsck_ref_report *report = fsck_report; 1260 + struct strbuf sb = STRBUF_INIT; 1261 + int ret = 0; 1262 + 1263 + strbuf_addstr(&sb, report->path); 1264 + 1265 + if (report->oid) 1266 + strbuf_addf(&sb, " -> (%s)", oid_to_hex(report->oid)); 1267 + else if (report->referent) 1268 + strbuf_addf(&sb, " -> (%s)", report->referent); 1269 + 1270 + if (msg_type == FSCK_WARN) 1271 + warning("%s: %s", sb.buf, message); 1272 + else 1273 + ret = error("%s: %s", sb.buf, message); 1274 + 1275 + strbuf_release(&sb); 1276 + return ret; 1277 + } 1278 + 1218 1279 static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done, 1219 1280 enum fsck_msg_id msg_missing, enum fsck_msg_id msg_type, 1220 1281 struct fsck_options *options, const char *blob_type) ··· 1268 1329 options, ".gitattributes"); 1269 1330 1270 1331 return ret; 1332 + } 1333 + 1334 + void fsck_options_clear(struct fsck_options *options) 1335 + { 1336 + free(options->msg_type); 1337 + oidset_clear(&options->skip_oids); 1338 + oidset_clear(&options->gitmodules_found); 1339 + oidset_clear(&options->gitmodules_done); 1340 + oidset_clear(&options->gitattributes_found); 1341 + oidset_clear(&options->gitattributes_done); 1342 + kh_clear_oid_map(options->object_names); 1271 1343 } 1272 1344 1273 1345 int git_fsck_config(const char *var, const char *value, ··· 1303 1375 * Custom error callbacks that are used in more than one place. 1304 1376 */ 1305 1377 1306 - int fsck_error_cb_print_missing_gitmodules(struct fsck_options *o, 1307 - const struct object_id *oid, 1308 - enum object_type object_type, 1309 - enum fsck_msg_type msg_type, 1310 - enum fsck_msg_id msg_id, 1311 - const char *message) 1378 + int fsck_objects_error_cb_print_missing_gitmodules(struct fsck_options *o, 1379 + void *fsck_report, 1380 + enum fsck_msg_type msg_type, 1381 + enum fsck_msg_id msg_id, 1382 + const char *message) 1312 1383 { 1313 1384 if (msg_id == FSCK_MSG_GITMODULES_MISSING) { 1314 - puts(oid_to_hex(oid)); 1385 + struct fsck_object_report *report = fsck_report; 1386 + puts(oid_to_hex(report->oid)); 1315 1387 return 0; 1316 1388 } 1317 - return fsck_error_function(o, oid, object_type, msg_type, msg_id, message); 1389 + return fsck_objects_error_function(o, fsck_report, 1390 + msg_type, msg_id, message); 1318 1391 }
+58 -18
fsck.h
··· 31 31 FUNC(BAD_NAME, ERROR) \ 32 32 FUNC(BAD_OBJECT_SHA1, ERROR) \ 33 33 FUNC(BAD_PARENT_SHA1, ERROR) \ 34 + FUNC(BAD_REF_FILETYPE, ERROR) \ 35 + FUNC(BAD_REF_NAME, ERROR) \ 34 36 FUNC(BAD_TIMEZONE, ERROR) \ 35 37 FUNC(BAD_TREE, ERROR) \ 36 38 FUNC(BAD_TREE_SHA1, ERROR) \ ··· 114 116 typedef int (*fsck_walk_func)(struct object *obj, enum object_type object_type, 115 117 void *data, struct fsck_options *options); 116 118 117 - /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */ 119 + /* 120 + * Callback for reporting errors either for objects or refs. The "fsck_report" 121 + * is a generic pointer that can be used to pass any information. 122 + */ 118 123 typedef int (*fsck_error)(struct fsck_options *o, 119 - const struct object_id *oid, enum object_type object_type, 124 + void *fsck_report, 120 125 enum fsck_msg_type msg_type, enum fsck_msg_id msg_id, 121 126 const char *message); 122 127 123 - int fsck_error_function(struct fsck_options *o, 124 - const struct object_id *oid, enum object_type object_type, 125 - enum fsck_msg_type msg_type, enum fsck_msg_id msg_id, 126 - const char *message); 127 - int fsck_error_cb_print_missing_gitmodules(struct fsck_options *o, 128 - const struct object_id *oid, 129 - enum object_type object_type, 130 - enum fsck_msg_type msg_type, 131 - enum fsck_msg_id msg_id, 132 - const char *message); 128 + int fsck_objects_error_function(struct fsck_options *o, 129 + void *fsck_report, 130 + enum fsck_msg_type msg_type, enum fsck_msg_id msg_id, 131 + const char *message); 132 + int fsck_objects_error_cb_print_missing_gitmodules(struct fsck_options *o, 133 + void *fsck_report, 134 + enum fsck_msg_type msg_type, 135 + enum fsck_msg_id msg_id, 136 + const char *message); 137 + 138 + int fsck_refs_error_function(struct fsck_options *options, 139 + void *fsck_report, 140 + enum fsck_msg_type msg_type, 141 + enum fsck_msg_id msg_id, 142 + const char *message); 143 + 144 + struct fsck_object_report { 145 + const struct object_id *oid; 146 + enum object_type object_type; 147 + }; 148 + 149 + struct fsck_ref_report { 150 + const char *path; 151 + const struct object_id *oid; 152 + const char *referent; 153 + }; 133 154 134 155 struct fsck_options { 135 156 fsck_walk_func walk; 136 157 fsck_error error_func; 137 - unsigned strict:1; 158 + unsigned strict; 159 + unsigned verbose; 138 160 enum fsck_msg_type *msg_type; 139 - struct oidset skiplist; 161 + struct oidset skip_oids; 140 162 struct oidset gitmodules_found; 141 163 struct oidset gitmodules_done; 142 164 struct oidset gitattributes_found; ··· 145 167 }; 146 168 147 169 #define FSCK_OPTIONS_DEFAULT { \ 148 - .skiplist = OIDSET_INIT, \ 170 + .skip_oids = OIDSET_INIT, \ 149 171 .gitmodules_found = OIDSET_INIT, \ 150 172 .gitmodules_done = OIDSET_INIT, \ 151 173 .gitattributes_found = OIDSET_INIT, \ 152 174 .gitattributes_done = OIDSET_INIT, \ 153 - .error_func = fsck_error_function \ 175 + .error_func = fsck_objects_error_function \ 154 176 } 155 177 #define FSCK_OPTIONS_STRICT { \ 156 178 .strict = 1, \ ··· 158 180 .gitmodules_done = OIDSET_INIT, \ 159 181 .gitattributes_found = OIDSET_INIT, \ 160 182 .gitattributes_done = OIDSET_INIT, \ 161 - .error_func = fsck_error_function, \ 183 + .error_func = fsck_objects_error_function, \ 162 184 } 163 185 #define FSCK_OPTIONS_MISSING_GITMODULES { \ 164 186 .strict = 1, \ ··· 166 188 .gitmodules_done = OIDSET_INIT, \ 167 189 .gitattributes_found = OIDSET_INIT, \ 168 190 .gitattributes_done = OIDSET_INIT, \ 169 - .error_func = fsck_error_cb_print_missing_gitmodules, \ 191 + .error_func = fsck_objects_error_cb_print_missing_gitmodules, \ 192 + } 193 + #define FSCK_REFS_OPTIONS_DEFAULT { \ 194 + .error_func = fsck_refs_error_function, \ 170 195 } 171 196 172 197 /* descend in all linked child objects ··· 208 233 * checks. 209 234 */ 210 235 int fsck_finish(struct fsck_options *options); 236 + 237 + /* 238 + * Clear the fsck_options struct, freeing any allocated memory. 239 + */ 240 + void fsck_options_clear(struct fsck_options *options); 241 + 242 + /* 243 + * Report an error or warning for refs. 244 + */ 245 + __attribute__((format (printf, 4, 5))) 246 + int fsck_report_ref(struct fsck_options *options, 247 + struct fsck_ref_report *report, 248 + enum fsck_msg_id msg_id, 249 + const char *fmt, ...); 250 + 211 251 212 252 /* 213 253 * Subsystem for storing human-readable names for each object.
+4 -5
object-file.c
··· 2470 2470 * give more context. 2471 2471 */ 2472 2472 static int hash_format_check_report(struct fsck_options *opts UNUSED, 2473 - const struct object_id *oid UNUSED, 2474 - enum object_type object_type UNUSED, 2475 - enum fsck_msg_type msg_type UNUSED, 2476 - enum fsck_msg_id msg_id UNUSED, 2477 - const char *message) 2473 + void *fsck_report UNUSED, 2474 + enum fsck_msg_type msg_type UNUSED, 2475 + enum fsck_msg_id msg_id UNUSED, 2476 + const char *message) 2478 2477 { 2479 2478 error(_("object fails fsck: %s"), message); 2480 2479 return 1;
+5
refs.c
··· 316 316 return check_or_sanitize_refname(refname, flags, NULL); 317 317 } 318 318 319 + int refs_fsck(struct ref_store *refs, struct fsck_options *o) 320 + { 321 + return refs->be->fsck(refs, o); 322 + } 323 + 319 324 void sanitize_refname_component(const char *refname, struct strbuf *out) 320 325 { 321 326 if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
+8
refs.h
··· 4 4 #include "commit.h" 5 5 #include "repository.h" 6 6 7 + struct fsck_options; 7 8 struct object_id; 8 9 struct ref_store; 9 10 struct strbuf; ··· 540 541 * repeated slashes are accepted. 541 542 */ 542 543 int check_refname_format(const char *refname, int flags); 544 + 545 + /* 546 + * Check the reference database for consistency. Return 0 if refs and 547 + * reflogs are consistent, and non-zero otherwise. The errors will be 548 + * written to stderr. 549 + */ 550 + int refs_fsck(struct ref_store *refs, struct fsck_options *o); 543 551 544 552 /* 545 553 * Apply the rules from check_refname_format, but mutate the result until it
+11
refs/debug.c
··· 419 419 return res; 420 420 } 421 421 422 + static int debug_fsck(struct ref_store *ref_store, 423 + struct fsck_options *o) 424 + { 425 + struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; 426 + int res = drefs->refs->be->fsck(drefs->refs, o); 427 + trace_printf_key(&trace_refs, "fsck: %d\n", res); 428 + return res; 429 + } 430 + 422 431 struct ref_storage_be refs_be_debug = { 423 432 .name = "debug", 424 433 .init = NULL, ··· 451 460 .create_reflog = debug_create_reflog, 452 461 .delete_reflog = debug_delete_reflog, 453 462 .reflog_expire = debug_reflog_expire, 463 + 464 + .fsck = debug_fsck, 454 465 };
+114 -1
refs/files-backend.c
··· 4 4 #include "../gettext.h" 5 5 #include "../hash.h" 6 6 #include "../hex.h" 7 + #include "../fsck.h" 7 8 #include "../refs.h" 8 9 #include "refs-internal.h" 9 10 #include "ref-cache.h" ··· 3419 3420 return ret; 3420 3421 } 3421 3422 3423 + /* 3424 + * For refs and reflogs, they share a unified interface when scanning 3425 + * the whole directory. This function is used as the callback for each 3426 + * regular file or symlink in the directory. 3427 + */ 3428 + typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store, 3429 + struct fsck_options *o, 3430 + const char *refs_check_dir, 3431 + struct dir_iterator *iter); 3432 + 3433 + static int files_fsck_refs_name(struct ref_store *ref_store UNUSED, 3434 + struct fsck_options *o, 3435 + const char *refs_check_dir, 3436 + struct dir_iterator *iter) 3437 + { 3438 + struct strbuf sb = STRBUF_INIT; 3439 + int ret = 0; 3440 + 3441 + /* 3442 + * Ignore the files ending with ".lock" as they may be lock files 3443 + * However, do not allow bare ".lock" files. 3444 + */ 3445 + if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock")) 3446 + goto cleanup; 3447 + 3448 + if (check_refname_format(iter->basename, REFNAME_ALLOW_ONELEVEL)) { 3449 + struct fsck_ref_report report = { .path = NULL }; 3450 + 3451 + strbuf_addf(&sb, "%s/%s", refs_check_dir, iter->relative_path); 3452 + report.path = sb.buf; 3453 + ret = fsck_report_ref(o, &report, 3454 + FSCK_MSG_BAD_REF_NAME, 3455 + "invalid refname format"); 3456 + } 3457 + 3458 + cleanup: 3459 + strbuf_release(&sb); 3460 + return ret; 3461 + } 3462 + 3463 + static int files_fsck_refs_dir(struct ref_store *ref_store, 3464 + struct fsck_options *o, 3465 + const char *refs_check_dir, 3466 + files_fsck_refs_fn *fsck_refs_fn) 3467 + { 3468 + struct strbuf sb = STRBUF_INIT; 3469 + struct dir_iterator *iter; 3470 + int iter_status; 3471 + int ret = 0; 3472 + 3473 + strbuf_addf(&sb, "%s/%s", ref_store->gitdir, refs_check_dir); 3474 + 3475 + iter = dir_iterator_begin(sb.buf, 0); 3476 + if (!iter) { 3477 + ret = error_errno(_("cannot open directory %s"), sb.buf); 3478 + goto out; 3479 + } 3480 + 3481 + while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) { 3482 + if (S_ISDIR(iter->st.st_mode)) { 3483 + continue; 3484 + } else if (S_ISREG(iter->st.st_mode) || 3485 + S_ISLNK(iter->st.st_mode)) { 3486 + if (o->verbose) 3487 + fprintf_ln(stderr, "Checking %s/%s", 3488 + refs_check_dir, iter->relative_path); 3489 + for (size_t i = 0; fsck_refs_fn[i]; i++) { 3490 + if (fsck_refs_fn[i](ref_store, o, refs_check_dir, iter)) 3491 + ret = -1; 3492 + } 3493 + } else { 3494 + struct fsck_ref_report report = { .path = iter->basename }; 3495 + if (fsck_report_ref(o, &report, 3496 + FSCK_MSG_BAD_REF_FILETYPE, 3497 + "unexpected file type")) 3498 + ret = -1; 3499 + } 3500 + } 3501 + 3502 + if (iter_status != ITER_DONE) 3503 + ret = error(_("failed to iterate over '%s'"), sb.buf); 3504 + 3505 + out: 3506 + strbuf_release(&sb); 3507 + return ret; 3508 + } 3509 + 3510 + static int files_fsck_refs(struct ref_store *ref_store, 3511 + struct fsck_options *o) 3512 + { 3513 + files_fsck_refs_fn fsck_refs_fn[]= { 3514 + files_fsck_refs_name, 3515 + NULL, 3516 + }; 3517 + 3518 + if (o->verbose) 3519 + fprintf_ln(stderr, _("Checking references consistency")); 3520 + return files_fsck_refs_dir(ref_store, o, "refs", fsck_refs_fn); 3521 + } 3522 + 3523 + static int files_fsck(struct ref_store *ref_store, 3524 + struct fsck_options *o) 3525 + { 3526 + struct files_ref_store *refs = 3527 + files_downcast(ref_store, REF_STORE_READ, "fsck"); 3528 + 3529 + return files_fsck_refs(ref_store, o) | 3530 + refs->packed_ref_store->be->fsck(refs->packed_ref_store, o); 3531 + } 3532 + 3422 3533 struct ref_storage_be refs_be_files = { 3423 3534 .name = "files", 3424 3535 .init = files_ref_store_init, ··· 3445 3556 .reflog_exists = files_reflog_exists, 3446 3557 .create_reflog = files_create_reflog, 3447 3558 .delete_reflog = files_delete_reflog, 3448 - .reflog_expire = files_reflog_expire 3559 + .reflog_expire = files_reflog_expire, 3560 + 3561 + .fsck = files_fsck, 3449 3562 };
+8
refs/packed-backend.c
··· 1733 1733 return empty_ref_iterator_begin(); 1734 1734 } 1735 1735 1736 + static int packed_fsck(struct ref_store *ref_store, 1737 + struct fsck_options *o) 1738 + { 1739 + return 0; 1740 + } 1741 + 1736 1742 struct ref_storage_be refs_be_packed = { 1737 1743 .name = "packed", 1738 1744 .init = packed_ref_store_init, ··· 1760 1766 .create_reflog = NULL, 1761 1767 .delete_reflog = NULL, 1762 1768 .reflog_expire = NULL, 1769 + 1770 + .fsck = packed_fsck, 1763 1771 };
+6
refs/refs-internal.h
··· 4 4 #include "refs.h" 5 5 #include "iterator.h" 6 6 7 + struct fsck_options; 7 8 struct ref_transaction; 8 9 9 10 /* ··· 651 652 typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname, 652 653 struct strbuf *referent); 653 654 655 + typedef int fsck_fn(struct ref_store *ref_store, 656 + struct fsck_options *o); 657 + 654 658 struct ref_storage_be { 655 659 const char *name; 656 660 ref_store_init_fn *init; ··· 678 682 create_reflog_fn *create_reflog; 679 683 delete_reflog_fn *delete_reflog; 680 684 reflog_expire_fn *reflog_expire; 685 + 686 + fsck_fn *fsck; 681 687 }; 682 688 683 689 extern struct ref_storage_be refs_be_files;
+8
refs/reftable-backend.c
··· 2309 2309 return ret; 2310 2310 } 2311 2311 2312 + static int reftable_be_fsck(struct ref_store *ref_store, 2313 + struct fsck_options *o) 2314 + { 2315 + return 0; 2316 + } 2317 + 2312 2318 struct ref_storage_be refs_be_reftable = { 2313 2319 .name = "reftable", 2314 2320 .init = reftable_be_init, ··· 2336 2342 .create_reflog = reftable_be_create_reflog, 2337 2343 .delete_reflog = reftable_be_delete_reflog, 2338 2344 .reflog_expire = reftable_be_reflog_expire, 2345 + 2346 + .fsck = reftable_be_fsck, 2339 2347 };
+92
t/t0602-reffiles-fsck.sh
··· 1 + #!/bin/sh 2 + 3 + test_description='Test reffiles backend consistency check' 4 + 5 + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 6 + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 7 + GIT_TEST_DEFAULT_REF_FORMAT=files 8 + export GIT_TEST_DEFAULT_REF_FORMAT 9 + TEST_PASSES_SANITIZE_LEAK=true 10 + 11 + . ./test-lib.sh 12 + 13 + test_expect_success 'ref name should be checked' ' 14 + test_when_finished "rm -rf repo" && 15 + git init repo && 16 + branch_dir_prefix=.git/refs/heads && 17 + tag_dir_prefix=.git/refs/tags && 18 + cd repo && 19 + 20 + git commit --allow-empty -m initial && 21 + git checkout -b branch-1 && 22 + git tag tag-1 && 23 + git commit --allow-empty -m second && 24 + git checkout -b branch-2 && 25 + git tag tag-2 && 26 + git tag multi_hierarchy/tag-2 && 27 + 28 + cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 && 29 + test_must_fail git refs verify 2>err && 30 + cat >expect <<-EOF && 31 + error: refs/heads/.branch-1: badRefName: invalid refname format 32 + EOF 33 + rm $branch_dir_prefix/.branch-1 && 34 + test_cmp expect err && 35 + 36 + cp $branch_dir_prefix/branch-1 $branch_dir_prefix/@ && 37 + test_must_fail git refs verify 2>err && 38 + cat >expect <<-EOF && 39 + error: refs/heads/@: badRefName: invalid refname format 40 + EOF 41 + rm $branch_dir_prefix/@ && 42 + test_cmp expect err && 43 + 44 + cp $tag_dir_prefix/multi_hierarchy/tag-2 $tag_dir_prefix/multi_hierarchy/@ && 45 + test_must_fail git refs verify 2>err && 46 + cat >expect <<-EOF && 47 + error: refs/tags/multi_hierarchy/@: badRefName: invalid refname format 48 + EOF 49 + rm $tag_dir_prefix/multi_hierarchy/@ && 50 + test_cmp expect err && 51 + 52 + cp $tag_dir_prefix/tag-1 $tag_dir_prefix/tag-1.lock && 53 + git refs verify 2>err && 54 + rm $tag_dir_prefix/tag-1.lock && 55 + test_must_be_empty err && 56 + 57 + cp $tag_dir_prefix/tag-1 $tag_dir_prefix/.lock && 58 + test_must_fail git refs verify 2>err && 59 + cat >expect <<-EOF && 60 + error: refs/tags/.lock: badRefName: invalid refname format 61 + EOF 62 + rm $tag_dir_prefix/.lock && 63 + test_cmp expect err 64 + ' 65 + 66 + test_expect_success 'ref name check should be adapted into fsck messages' ' 67 + test_when_finished "rm -rf repo" && 68 + git init repo && 69 + branch_dir_prefix=.git/refs/heads && 70 + tag_dir_prefix=.git/refs/tags && 71 + cd repo && 72 + git commit --allow-empty -m initial && 73 + git checkout -b branch-1 && 74 + git tag tag-1 && 75 + git commit --allow-empty -m second && 76 + git checkout -b branch-2 && 77 + git tag tag-2 && 78 + 79 + cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 && 80 + git -c fsck.badRefName=warn refs verify 2>err && 81 + cat >expect <<-EOF && 82 + warning: refs/heads/.branch-1: badRefName: invalid refname format 83 + EOF 84 + rm $branch_dir_prefix/.branch-1 && 85 + test_cmp expect err && 86 + 87 + cp $branch_dir_prefix/branch-1 $branch_dir_prefix/@ && 88 + git -c fsck.badRefName=ignore refs verify 2>err && 89 + test_must_be_empty err 90 + ' 91 + 92 + test_done