fsck_walk(): optionally name objects on the go

If fsck_options->name_objects is initialized, and if it already has name(s) for the object(s) that are to be the starting point(s) for fsck_walk(), then that function will now add names for the objects that were walked. This will be highly useful for teaching git-fsck to identify root causes for broken links, which is the task for the next patch in this series. Note that this patch opts for decorating the objects with plain strings instead of full-blown structs (à la `struct rev_name` in the code of the `git name-rev` command), for several reasons: - the code is much simpler than if it had to work with structs that describe arbitrarily long names such as "master~14^2~5:builtin/am.c", - the string processing is actually quite light-weight compared to the rest of fsck's operation, - the caller of fsck_walk() is expected to provide names for the starting points, and using plain and simple strings is just the easiest way to do that. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jul 17, 2016 at 12:59 UTC 7b35efd734e501f9e4692768a8b6aea818c0c93e
2 files changed +84 -4
fsck.c
+83 -4
@@ -9,6 +9,7 @@
9 #include "refs.h"
10 #include "utf8.h"
11 #include "sha1-array.h"
12 +#include "decorate.h"
13
14 #define FSCK_FATAL -1
15 #define FSCK_INFO -2
@@ -297,25 +298,64 @@ static int report(struct fsck_options *options, struct object *object,
298 return result;
299 }
300
301 +static char *get_object_name(struct fsck_options *options, struct object *obj)
302 +{
303 + if (!options->object_names)
304 + return NULL;
305 + return lookup_decoration(options->object_names, obj);
306 +}
307 +
308 +static void put_object_name(struct fsck_options *options, struct object *obj,
309 + const char *fmt, ...)
310 +{
311 + va_list ap;
312 + struct strbuf buf = STRBUF_INIT;
313 + char *existing;
314 +
315 + if (!options->object_names)
316 + return;
317 + existing = lookup_decoration(options->object_names, obj);
318 + if (existing)
319 + return;
320 + va_start(ap, fmt);
321 + strbuf_vaddf(&buf, fmt, ap);
322 + add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
323 + va_end(ap);
324 +}
325 +
326 static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
327 {
328 struct tree_desc desc;
329 struct name_entry entry;
330 int res = 0;
331 + const char *name;
332
333 if (parse_tree(tree))
334 return -1;
335
336 + name = get_object_name(options, &tree->object);
337 init_tree_desc(&desc, tree->buffer, tree->size);
338 while (tree_entry(&desc, &entry)) {
339 + struct object *obj;
340 int result;
341
342 if (S_ISGITLINK(entry.mode))
343 continue;
315 - if (S_ISDIR(entry.mode))
316 - result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
317 - else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
318 - result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
344 +
345 + if (S_ISDIR(entry.mode)) {
346 + obj = &lookup_tree(entry.oid->hash)->object;
347 + if (name)
348 + put_object_name(options, obj, "%s%s/", name,
349 + entry.path);
350 + result = options->walk(obj, OBJ_TREE, data, options);
351 + }
352 + else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
353 + obj = &lookup_blob(entry.oid->hash)->object;
354 + if (name)
355 + put_object_name(options, obj, "%s%s", name,
356 + entry.path);
357 + result = options->walk(obj, OBJ_BLOB, data, options);
358 + }
359 else {
360 result = error("in tree %s: entry %s has bad mode %.6o",
361 oid_to_hex(&tree->object.oid), entry.path, entry.mode);
@@ -330,20 +370,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
370
371 static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
372 {
373 + int counter = 0, generation = 0, name_prefix_len = 0;
374 struct commit_list *parents;
375 int res;
376 int result;
377 + const char *name;
378
379 if (parse_commit(commit))
380 return -1;
381
382 + name = get_object_name(options, &commit->object);
383 + if (name)
384 + put_object_name(options, &commit->tree->object, "%s:", name);
385 +
386 result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
387 if (result < 0)
388 return result;
389 res = result;
390
391 parents = commit->parents;
392 + if (name && parents) {
393 + int len = strlen(name), power;
394 +
395 + if (len && name[len - 1] == '^') {
396 + generation = 1;
397 + name_prefix_len = len - 1;
398 + }
399 + else { /* parse ~<generation> suffix */
400 + for (generation = 0, power = 1;
401 + len && isdigit(name[len - 1]);
402 + power *= 10)
403 + generation += power * (name[--len] - '0');
404 + if (power > 1 && len && name[len - 1] == '~')
405 + name_prefix_len = len - 1;
406 + }
407 + }
408 +
409 while (parents) {
410 + if (name) {
411 + struct object *obj = &parents->item->object;
412 +
413 + if (++counter > 1)
414 + put_object_name(options, obj, "%s^%d",
415 + name, counter);
416 + else if (generation > 0)
417 + put_object_name(options, obj, "%.*s~%d",
418 + name_prefix_len, name, generation + 1);
419 + else
420 + put_object_name(options, obj, "%s^", name);
421 + }
422 result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
423 if (result < 0)
424 return result;
@@ -356,8 +431,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
431
432 static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
433 {
434 + char *name = get_object_name(options, &tag->object);
435 +
436 if (parse_tag(tag))
437 return -1;
438 + if (name)
439 + put_object_name(options, tag->tagged, "%s", name);
440 return options->walk(tag->tagged, OBJ_ANY, data, options);
441 }
442
fsck.h
+1
@@ -33,6 +33,7 @@ struct fsck_options {
33 unsigned strict:1;
34 int *msg_type;
35 struct sha1_array *skiplist;
36 + struct decoration *object_names;
37 };
38
39 #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }