Merge branch 'js/fsck-name-object'
When "git fsck" reports a broken link (e.g. a tree object contains a blob that does not exist), both containing object and the object that is referred to were reported with their 40-hex object names. The command learned the "--name-objects" option to show the path to the containing object from existing refs (e.g. "HEAD~24^2:file.txt"). * js/fsck-name-object: fsck: optionally show more helpful info for broken links fsck: give the error function a chance to see the fsck_options fsck_walk(): optionally name objects on the go fsck: refactor how to describe objectsmaint
commit
9db3979784
|
@ -11,7 +11,8 @@ SYNOPSIS
|
||||||
[verse]
|
[verse]
|
||||||
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
|
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
|
||||||
[--[no-]full] [--strict] [--verbose] [--lost-found]
|
[--[no-]full] [--strict] [--verbose] [--lost-found]
|
||||||
[--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]
|
[--[no-]dangling] [--[no-]progress] [--connectivity-only]
|
||||||
|
[--[no-]name-objects] [<object>*]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -82,6 +83,12 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs
|
||||||
a blob, the contents are written into the file, rather than
|
a blob, the contents are written into the file, rather than
|
||||||
its object name.
|
its object name.
|
||||||
|
|
||||||
|
--name-objects::
|
||||||
|
When displaying names of reachable objects, in addition to the
|
||||||
|
SHA-1 also display a name that describes *how* they are reachable,
|
||||||
|
compatible with linkgit:git-rev-parse[1], e.g.
|
||||||
|
`HEAD@{1234567890}~25^2:src/`.
|
||||||
|
|
||||||
--[no-]progress::
|
--[no-]progress::
|
||||||
Progress status is reported on the standard error stream by
|
Progress status is reported on the standard error stream by
|
||||||
default when it is attached to a terminal, unless
|
default when it is attached to a terminal, unless
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
#include "streaming.h"
|
#include "streaming.h"
|
||||||
|
#include "decorate.h"
|
||||||
|
|
||||||
#define REACHABLE 0x0001
|
#define REACHABLE 0x0001
|
||||||
#define SEEN 0x0002
|
#define SEEN 0x0002
|
||||||
|
@ -35,11 +36,26 @@ static int write_lost_and_found;
|
||||||
static int verbose;
|
static int verbose;
|
||||||
static int show_progress = -1;
|
static int show_progress = -1;
|
||||||
static int show_dangling = 1;
|
static int show_dangling = 1;
|
||||||
|
static int name_objects;
|
||||||
#define ERROR_OBJECT 01
|
#define ERROR_OBJECT 01
|
||||||
#define ERROR_REACHABLE 02
|
#define ERROR_REACHABLE 02
|
||||||
#define ERROR_PACK 04
|
#define ERROR_PACK 04
|
||||||
#define ERROR_REFS 010
|
#define ERROR_REFS 010
|
||||||
|
|
||||||
|
static const char *describe_object(struct object *obj)
|
||||||
|
{
|
||||||
|
static struct strbuf buf = STRBUF_INIT;
|
||||||
|
char *name = name_objects ?
|
||||||
|
lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
|
||||||
|
|
||||||
|
strbuf_reset(&buf);
|
||||||
|
strbuf_addstr(&buf, oid_to_hex(&obj->oid));
|
||||||
|
if (name)
|
||||||
|
strbuf_addf(&buf, " (%s)", name);
|
||||||
|
|
||||||
|
return buf.buf;
|
||||||
|
}
|
||||||
|
|
||||||
static int fsck_config(const char *var, const char *value, void *cb)
|
static int fsck_config(const char *var, const char *value, void *cb)
|
||||||
{
|
{
|
||||||
if (strcmp(var, "fsck.skiplist") == 0) {
|
if (strcmp(var, "fsck.skiplist") == 0) {
|
||||||
|
@ -67,7 +83,7 @@ static void objreport(struct object *obj, const char *msg_type,
|
||||||
const char *err)
|
const char *err)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s in %s %s: %s\n",
|
fprintf(stderr, "%s in %s %s: %s\n",
|
||||||
msg_type, typename(obj->type), oid_to_hex(&obj->oid), err);
|
msg_type, typename(obj->type), describe_object(obj), err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int objerror(struct object *obj, const char *err)
|
static int objerror(struct object *obj, const char *err)
|
||||||
|
@ -77,7 +93,8 @@ static int objerror(struct object *obj, const char *err)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsck_error_func(struct object *obj, int type, const char *message)
|
static int fsck_error_func(struct fsck_options *o,
|
||||||
|
struct object *obj, int type, const char *message)
|
||||||
{
|
{
|
||||||
objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
|
objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
|
||||||
return (type == FSCK_WARN) ? 0 : 1;
|
return (type == FSCK_WARN) ? 0 : 1;
|
||||||
|
@ -97,7 +114,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
/* ... these references to parent->fld are safe here */
|
/* ... these references to parent->fld are safe here */
|
||||||
printf("broken link from %7s %s\n",
|
printf("broken link from %7s %s\n",
|
||||||
typename(parent->type), oid_to_hex(&parent->oid));
|
typename(parent->type), describe_object(parent));
|
||||||
printf("broken link from %7s %s\n",
|
printf("broken link from %7s %s\n",
|
||||||
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
|
(type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
|
||||||
errors_found |= ERROR_REACHABLE;
|
errors_found |= ERROR_REACHABLE;
|
||||||
|
@ -114,9 +131,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
|
||||||
if (!(obj->flags & HAS_OBJ)) {
|
if (!(obj->flags & HAS_OBJ)) {
|
||||||
if (parent && !has_object_file(&obj->oid)) {
|
if (parent && !has_object_file(&obj->oid)) {
|
||||||
printf("broken link from %7s %s\n",
|
printf("broken link from %7s %s\n",
|
||||||
typename(parent->type), oid_to_hex(&parent->oid));
|
typename(parent->type), describe_object(parent));
|
||||||
printf(" to %7s %s\n",
|
printf(" to %7s %s\n",
|
||||||
typename(obj->type), oid_to_hex(&obj->oid));
|
typename(obj->type), describe_object(obj));
|
||||||
errors_found |= ERROR_REACHABLE;
|
errors_found |= ERROR_REACHABLE;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -190,7 +207,8 @@ static void check_reachable_object(struct object *obj)
|
||||||
return; /* it is in pack - forget about it */
|
return; /* it is in pack - forget about it */
|
||||||
if (connectivity_only && has_object_file(&obj->oid))
|
if (connectivity_only && has_object_file(&obj->oid))
|
||||||
return;
|
return;
|
||||||
printf("missing %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
|
printf("missing %s %s\n", typename(obj->type),
|
||||||
|
describe_object(obj));
|
||||||
errors_found |= ERROR_REACHABLE;
|
errors_found |= ERROR_REACHABLE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -215,7 +233,8 @@ static void check_unreachable_object(struct object *obj)
|
||||||
* since this is something that is prunable.
|
* since this is something that is prunable.
|
||||||
*/
|
*/
|
||||||
if (show_unreachable) {
|
if (show_unreachable) {
|
||||||
printf("unreachable %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
|
printf("unreachable %s %s\n", typename(obj->type),
|
||||||
|
describe_object(obj));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,11 +253,11 @@ static void check_unreachable_object(struct object *obj)
|
||||||
if (!obj->used) {
|
if (!obj->used) {
|
||||||
if (show_dangling)
|
if (show_dangling)
|
||||||
printf("dangling %s %s\n", typename(obj->type),
|
printf("dangling %s %s\n", typename(obj->type),
|
||||||
oid_to_hex(&obj->oid));
|
describe_object(obj));
|
||||||
if (write_lost_and_found) {
|
if (write_lost_and_found) {
|
||||||
char *filename = git_pathdup("lost-found/%s/%s",
|
char *filename = git_pathdup("lost-found/%s/%s",
|
||||||
obj->type == OBJ_COMMIT ? "commit" : "other",
|
obj->type == OBJ_COMMIT ? "commit" : "other",
|
||||||
oid_to_hex(&obj->oid));
|
describe_object(obj));
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
if (safe_create_leading_directories_const(filename)) {
|
if (safe_create_leading_directories_const(filename)) {
|
||||||
|
@ -252,7 +271,7 @@ static void check_unreachable_object(struct object *obj)
|
||||||
if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
|
if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
|
||||||
die_errno("Could not write '%s'", filename);
|
die_errno("Could not write '%s'", filename);
|
||||||
} else
|
} else
|
||||||
fprintf(f, "%s\n", oid_to_hex(&obj->oid));
|
fprintf(f, "%s\n", describe_object(obj));
|
||||||
if (fclose(f))
|
if (fclose(f))
|
||||||
die_errno("Could not finish '%s'",
|
die_errno("Could not finish '%s'",
|
||||||
filename);
|
filename);
|
||||||
|
@ -271,7 +290,7 @@ static void check_unreachable_object(struct object *obj)
|
||||||
static void check_object(struct object *obj)
|
static void check_object(struct object *obj)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf(stderr, "Checking %s\n", oid_to_hex(&obj->oid));
|
fprintf(stderr, "Checking %s\n", describe_object(obj));
|
||||||
|
|
||||||
if (obj->flags & REACHABLE)
|
if (obj->flags & REACHABLE)
|
||||||
check_reachable_object(obj);
|
check_reachable_object(obj);
|
||||||
|
@ -307,7 +326,7 @@ static int fsck_obj(struct object *obj)
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf(stderr, "Checking %s %s\n",
|
fprintf(stderr, "Checking %s %s\n",
|
||||||
typename(obj->type), oid_to_hex(&obj->oid));
|
typename(obj->type), describe_object(obj));
|
||||||
|
|
||||||
if (fsck_walk(obj, NULL, &fsck_obj_options))
|
if (fsck_walk(obj, NULL, &fsck_obj_options))
|
||||||
objerror(obj, "broken links");
|
objerror(obj, "broken links");
|
||||||
|
@ -326,15 +345,17 @@ static int fsck_obj(struct object *obj)
|
||||||
free_commit_buffer(commit);
|
free_commit_buffer(commit);
|
||||||
|
|
||||||
if (!commit->parents && show_root)
|
if (!commit->parents && show_root)
|
||||||
printf("root %s\n", oid_to_hex(&commit->object.oid));
|
printf("root %s\n", describe_object(&commit->object));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->type == OBJ_TAG) {
|
if (obj->type == OBJ_TAG) {
|
||||||
struct tag *tag = (struct tag *) obj;
|
struct tag *tag = (struct tag *) obj;
|
||||||
|
|
||||||
if (show_tags && tag->tagged) {
|
if (show_tags && tag->tagged) {
|
||||||
printf("tagged %s %s", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));
|
printf("tagged %s %s", typename(tag->tagged->type),
|
||||||
printf(" (%s) in %s\n", tag->tag, oid_to_hex(&tag->object.oid));
|
describe_object(tag->tagged));
|
||||||
|
printf(" (%s) in %s\n", tag->tag,
|
||||||
|
describe_object(&tag->object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,13 +389,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
|
||||||
|
|
||||||
static int default_refs;
|
static int default_refs;
|
||||||
|
|
||||||
static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
|
static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
|
||||||
|
unsigned long timestamp)
|
||||||
{
|
{
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
|
|
||||||
if (!is_null_sha1(sha1)) {
|
if (!is_null_sha1(sha1)) {
|
||||||
obj = lookup_object(sha1);
|
obj = lookup_object(sha1);
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
if (timestamp && name_objects)
|
||||||
|
add_decoration(fsck_walk_options.object_names,
|
||||||
|
obj,
|
||||||
|
xstrfmt("%s@{%ld}", refname, timestamp));
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
mark_object_reachable(obj);
|
mark_object_reachable(obj);
|
||||||
} else {
|
} else {
|
||||||
|
@ -394,8 +420,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||||
fprintf(stderr, "Checking reflog %s->%s\n",
|
fprintf(stderr, "Checking reflog %s->%s\n",
|
||||||
sha1_to_hex(osha1), sha1_to_hex(nsha1));
|
sha1_to_hex(osha1), sha1_to_hex(nsha1));
|
||||||
|
|
||||||
fsck_handle_reflog_sha1(refname, osha1);
|
fsck_handle_reflog_sha1(refname, osha1, 0);
|
||||||
fsck_handle_reflog_sha1(refname, nsha1);
|
fsck_handle_reflog_sha1(refname, nsha1, timestamp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,6 +450,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
|
||||||
}
|
}
|
||||||
default_refs++;
|
default_refs++;
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
|
if (name_objects)
|
||||||
|
add_decoration(fsck_walk_options.object_names,
|
||||||
|
obj, xstrdup(refname));
|
||||||
mark_object_reachable(obj);
|
mark_object_reachable(obj);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -539,6 +568,9 @@ static int fsck_cache_tree(struct cache_tree *it)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
|
if (name_objects)
|
||||||
|
add_decoration(fsck_walk_options.object_names,
|
||||||
|
obj, xstrdup(":"));
|
||||||
mark_object_reachable(obj);
|
mark_object_reachable(obj);
|
||||||
if (obj->type != OBJ_TREE)
|
if (obj->type != OBJ_TREE)
|
||||||
err |= objerror(obj, "non-tree in cache-tree");
|
err |= objerror(obj, "non-tree in cache-tree");
|
||||||
|
@ -567,6 +599,7 @@ static struct option fsck_opts[] = {
|
||||||
OPT_BOOL(0, "lost-found", &write_lost_and_found,
|
OPT_BOOL(0, "lost-found", &write_lost_and_found,
|
||||||
N_("write dangling objects in .git/lost-found")),
|
N_("write dangling objects in .git/lost-found")),
|
||||||
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
|
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
|
||||||
|
OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -596,6 +629,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||||
include_reflogs = 0;
|
include_reflogs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name_objects)
|
||||||
|
fsck_walk_options.object_names =
|
||||||
|
xcalloc(1, sizeof(struct decoration));
|
||||||
|
|
||||||
git_config(fsck_config, NULL);
|
git_config(fsck_config, NULL);
|
||||||
|
|
||||||
fsck_head_link();
|
fsck_head_link();
|
||||||
|
@ -651,6 +688,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
|
if (name_objects)
|
||||||
|
add_decoration(fsck_walk_options.object_names,
|
||||||
|
obj, xstrdup(arg));
|
||||||
mark_object_reachable(obj);
|
mark_object_reachable(obj);
|
||||||
heads++;
|
heads++;
|
||||||
continue;
|
continue;
|
||||||
|
@ -683,6 +723,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
obj = &blob->object;
|
obj = &blob->object;
|
||||||
obj->used = 1;
|
obj->used = 1;
|
||||||
|
if (name_objects)
|
||||||
|
add_decoration(fsck_walk_options.object_names,
|
||||||
|
obj,
|
||||||
|
xstrfmt(":%s", active_cache[i]->name));
|
||||||
mark_object_reachable(obj);
|
mark_object_reachable(obj);
|
||||||
}
|
}
|
||||||
if (active_cache_tree)
|
if (active_cache_tree)
|
||||||
|
|
113
fsck.c
113
fsck.c
|
@ -9,6 +9,7 @@
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
#include "sha1-array.h"
|
#include "sha1-array.h"
|
||||||
|
#include "decorate.h"
|
||||||
|
|
||||||
#define FSCK_FATAL -1
|
#define FSCK_FATAL -1
|
||||||
#define FSCK_INFO -2
|
#define FSCK_INFO -2
|
||||||
|
@ -290,35 +291,87 @@ static int report(struct fsck_options *options, struct object *object,
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
strbuf_vaddf(&sb, fmt, ap);
|
strbuf_vaddf(&sb, fmt, ap);
|
||||||
result = options->error_func(object, msg_type, sb.buf);
|
result = options->error_func(options, object, msg_type, sb.buf);
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *get_object_name(struct fsck_options *options, struct object *obj)
|
||||||
|
{
|
||||||
|
if (!options->object_names)
|
||||||
|
return NULL;
|
||||||
|
return lookup_decoration(options->object_names, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_object_name(struct fsck_options *options, struct object *obj,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
char *existing;
|
||||||
|
|
||||||
|
if (!options->object_names)
|
||||||
|
return;
|
||||||
|
existing = lookup_decoration(options->object_names, obj);
|
||||||
|
if (existing)
|
||||||
|
return;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strbuf_vaddf(&buf, fmt, ap);
|
||||||
|
add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *describe_object(struct fsck_options *o, struct object *obj)
|
||||||
|
{
|
||||||
|
static struct strbuf buf = STRBUF_INIT;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
strbuf_reset(&buf);
|
||||||
|
strbuf_addstr(&buf, oid_to_hex(&obj->oid));
|
||||||
|
if (o->object_names && (name = lookup_decoration(o->object_names, obj)))
|
||||||
|
strbuf_addf(&buf, " (%s)", name);
|
||||||
|
|
||||||
|
return buf.buf;
|
||||||
|
}
|
||||||
|
|
||||||
static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
|
static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
|
||||||
{
|
{
|
||||||
struct tree_desc desc;
|
struct tree_desc desc;
|
||||||
struct name_entry entry;
|
struct name_entry entry;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
if (parse_tree(tree))
|
if (parse_tree(tree))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
name = get_object_name(options, &tree->object);
|
||||||
init_tree_desc(&desc, tree->buffer, tree->size);
|
init_tree_desc(&desc, tree->buffer, tree->size);
|
||||||
while (tree_entry(&desc, &entry)) {
|
while (tree_entry(&desc, &entry)) {
|
||||||
|
struct object *obj;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (S_ISGITLINK(entry.mode))
|
if (S_ISGITLINK(entry.mode))
|
||||||
continue;
|
continue;
|
||||||
if (S_ISDIR(entry.mode))
|
|
||||||
result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
|
if (S_ISDIR(entry.mode)) {
|
||||||
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
|
obj = &lookup_tree(entry.oid->hash)->object;
|
||||||
result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
|
if (name)
|
||||||
|
put_object_name(options, obj, "%s%s/", name,
|
||||||
|
entry.path);
|
||||||
|
result = options->walk(obj, OBJ_TREE, data, options);
|
||||||
|
}
|
||||||
|
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
|
||||||
|
obj = &lookup_blob(entry.oid->hash)->object;
|
||||||
|
if (name)
|
||||||
|
put_object_name(options, obj, "%s%s", name,
|
||||||
|
entry.path);
|
||||||
|
result = options->walk(obj, OBJ_BLOB, data, options);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
result = error("in tree %s: entry %s has bad mode %.6o",
|
result = error("in tree %s: entry %s has bad mode %.6o",
|
||||||
oid_to_hex(&tree->object.oid), entry.path, entry.mode);
|
describe_object(options, &tree->object), entry.path, entry.mode);
|
||||||
}
|
}
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
@ -330,20 +383,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
|
||||||
|
|
||||||
static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
|
static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
|
||||||
{
|
{
|
||||||
|
int counter = 0, generation = 0, name_prefix_len = 0;
|
||||||
struct commit_list *parents;
|
struct commit_list *parents;
|
||||||
int res;
|
int res;
|
||||||
int result;
|
int result;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
if (parse_commit(commit))
|
if (parse_commit(commit))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
name = get_object_name(options, &commit->object);
|
||||||
|
if (name)
|
||||||
|
put_object_name(options, &commit->tree->object, "%s:", name);
|
||||||
|
|
||||||
result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
|
result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
res = result;
|
res = result;
|
||||||
|
|
||||||
parents = commit->parents;
|
parents = commit->parents;
|
||||||
|
if (name && parents) {
|
||||||
|
int len = strlen(name), power;
|
||||||
|
|
||||||
|
if (len && name[len - 1] == '^') {
|
||||||
|
generation = 1;
|
||||||
|
name_prefix_len = len - 1;
|
||||||
|
}
|
||||||
|
else { /* parse ~<generation> suffix */
|
||||||
|
for (generation = 0, power = 1;
|
||||||
|
len && isdigit(name[len - 1]);
|
||||||
|
power *= 10)
|
||||||
|
generation += power * (name[--len] - '0');
|
||||||
|
if (power > 1 && len && name[len - 1] == '~')
|
||||||
|
name_prefix_len = len - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (parents) {
|
while (parents) {
|
||||||
|
if (name) {
|
||||||
|
struct object *obj = &parents->item->object;
|
||||||
|
|
||||||
|
if (++counter > 1)
|
||||||
|
put_object_name(options, obj, "%s^%d",
|
||||||
|
name, counter);
|
||||||
|
else if (generation > 0)
|
||||||
|
put_object_name(options, obj, "%.*s~%d",
|
||||||
|
name_prefix_len, name, generation + 1);
|
||||||
|
else
|
||||||
|
put_object_name(options, obj, "%s^", name);
|
||||||
|
}
|
||||||
result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
|
result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
@ -356,8 +444,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
|
||||||
|
|
||||||
static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
|
static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
|
||||||
{
|
{
|
||||||
|
char *name = get_object_name(options, &tag->object);
|
||||||
|
|
||||||
if (parse_tag(tag))
|
if (parse_tag(tag))
|
||||||
return -1;
|
return -1;
|
||||||
|
if (name)
|
||||||
|
put_object_name(options, tag->tagged, "%s", name);
|
||||||
return options->walk(tag->tagged, OBJ_ANY, data, options);
|
return options->walk(tag->tagged, OBJ_ANY, data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
|
||||||
case OBJ_TAG:
|
case OBJ_TAG:
|
||||||
return fsck_walk_tag((struct tag *)obj, data, options);
|
return fsck_walk_tag((struct tag *)obj, data, options);
|
||||||
default:
|
default:
|
||||||
error("Unknown object type for %s", oid_to_hex(&obj->oid));
|
error("Unknown object type for %s", describe_object(options, obj));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -818,12 +910,13 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
|
||||||
obj->type);
|
obj->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fsck_error_function(struct object *obj, int msg_type, const char *message)
|
int fsck_error_function(struct fsck_options *o,
|
||||||
|
struct object *obj, int msg_type, const char *message)
|
||||||
{
|
{
|
||||||
if (msg_type == FSCK_WARN) {
|
if (msg_type == FSCK_WARN) {
|
||||||
warning("object %s: %s", oid_to_hex(&obj->oid), message);
|
warning("object %s: %s", describe_object(o, obj), message);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error("object %s: %s", oid_to_hex(&obj->oid), message);
|
error("object %s: %s", describe_object(o, obj), message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
7
fsck.h
7
fsck.h
|
@ -23,9 +23,11 @@ int is_valid_msg_type(const char *msg_id, const char *msg_type);
|
||||||
typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);
|
typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);
|
||||||
|
|
||||||
/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
|
/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
|
||||||
typedef int (*fsck_error)(struct object *obj, int type, const char *message);
|
typedef int (*fsck_error)(struct fsck_options *o,
|
||||||
|
struct object *obj, int type, const char *message);
|
||||||
|
|
||||||
int fsck_error_function(struct object *obj, int type, const char *message);
|
int fsck_error_function(struct fsck_options *o,
|
||||||
|
struct object *obj, int type, const char *message);
|
||||||
|
|
||||||
struct fsck_options {
|
struct fsck_options {
|
||||||
fsck_walk_func walk;
|
fsck_walk_func walk;
|
||||||
|
@ -33,6 +35,7 @@ struct fsck_options {
|
||||||
unsigned strict:1;
|
unsigned strict:1;
|
||||||
int *msg_type;
|
int *msg_type;
|
||||||
struct sha1_array *skiplist;
|
struct sha1_array *skiplist;
|
||||||
|
struct decoration *object_names;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }
|
#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }
|
||||||
|
|
|
@ -523,4 +523,26 @@ test_expect_success 'fsck --connectivity-only' '
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
remove_loose_object () {
|
||||||
|
sha1="$(git rev-parse "$1")" &&
|
||||||
|
remainder=${sha1#??} &&
|
||||||
|
firsttwo=${sha1%$remainder} &&
|
||||||
|
rm .git/objects/$firsttwo/$remainder
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'fsck --name-objects' '
|
||||||
|
rm -rf name-objects &&
|
||||||
|
git init name-objects &&
|
||||||
|
(
|
||||||
|
cd name-objects &&
|
||||||
|
test_commit julius caesar.t &&
|
||||||
|
test_commit augustus &&
|
||||||
|
test_commit caesar &&
|
||||||
|
remove_loose_object $(git rev-parse julius:caesar.t) &&
|
||||||
|
test_must_fail git fsck --name-objects >out &&
|
||||||
|
tree=$(git rev-parse --verify julius:) &&
|
||||||
|
grep "$tree (\(refs/heads/master\|HEAD\)@{[0-9]*}:" out
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in New Issue