fsck: optionally show more helpful info for broken links
When reporting broken links between commits/trees/blobs, it would be quite helpful at times if the user would be told how the object is supposed to be reachable. With the new --name-objects option, git-fsck will try to do exactly that: name the objects in a way that shows how they are reachable. For example, when some reflog got corrupted and a blob is missing that should not be, the user might want to remove the corresponding reflog entry. This option helps them find that entry: `git fsck` will now report something like this: broken link from tree b5eb6ff... (refs/stash@{<date>}~37:) to blob ec5cf80... Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
1cd772cc41
commit
90cf590f53
@ -11,7 +11,8 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
|
||||
[--[no-]full] [--strict] [--verbose] [--lost-found]
|
||||
[--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]
|
||||
[--[no-]dangling] [--[no-]progress] [--connectivity-only]
|
||||
[--[no-]name-objects] [<object>*]
|
||||
|
||||
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
|
||||
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::
|
||||
Progress status is reported on the standard error stream by
|
||||
default when it is attached to a terminal, unless
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "dir.h"
|
||||
#include "progress.h"
|
||||
#include "streaming.h"
|
||||
#include "decorate.h"
|
||||
|
||||
#define REACHABLE 0x0001
|
||||
#define SEEN 0x0002
|
||||
@ -35,6 +36,7 @@ static int write_lost_and_found;
|
||||
static int verbose;
|
||||
static int show_progress = -1;
|
||||
static int show_dangling = 1;
|
||||
static int name_objects;
|
||||
#define ERROR_OBJECT 01
|
||||
#define ERROR_REACHABLE 02
|
||||
#define ERROR_PACK 04
|
||||
@ -42,7 +44,16 @@ static int show_dangling = 1;
|
||||
|
||||
static const char *describe_object(struct object *obj)
|
||||
{
|
||||
return oid_to_hex(&obj->oid);
|
||||
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)
|
||||
@ -378,13 +389,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
|
||||
|
||||
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;
|
||||
|
||||
if (!is_null_sha1(sha1)) {
|
||||
obj = lookup_object(sha1);
|
||||
if (obj) {
|
||||
if (timestamp && name_objects)
|
||||
add_decoration(fsck_walk_options.object_names,
|
||||
obj,
|
||||
xstrfmt("%s@{%ld}", refname, timestamp));
|
||||
obj->used = 1;
|
||||
mark_object_reachable(obj);
|
||||
} else {
|
||||
@ -404,8 +420,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
|
||||
fprintf(stderr, "Checking reflog %s->%s\n",
|
||||
sha1_to_hex(osha1), sha1_to_hex(nsha1));
|
||||
|
||||
fsck_handle_reflog_sha1(refname, osha1);
|
||||
fsck_handle_reflog_sha1(refname, nsha1);
|
||||
fsck_handle_reflog_sha1(refname, osha1, 0);
|
||||
fsck_handle_reflog_sha1(refname, nsha1, timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -434,6 +450,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
|
||||
}
|
||||
default_refs++;
|
||||
obj->used = 1;
|
||||
if (name_objects)
|
||||
add_decoration(fsck_walk_options.object_names,
|
||||
obj, xstrdup(refname));
|
||||
mark_object_reachable(obj);
|
||||
|
||||
return 0;
|
||||
@ -549,6 +568,9 @@ static int fsck_cache_tree(struct cache_tree *it)
|
||||
return 1;
|
||||
}
|
||||
obj->used = 1;
|
||||
if (name_objects)
|
||||
add_decoration(fsck_walk_options.object_names,
|
||||
obj, xstrdup(":"));
|
||||
mark_object_reachable(obj);
|
||||
if (obj->type != OBJ_TREE)
|
||||
err |= objerror(obj, "non-tree in cache-tree");
|
||||
@ -577,6 +599,7 @@ static struct option fsck_opts[] = {
|
||||
OPT_BOOL(0, "lost-found", &write_lost_and_found,
|
||||
N_("write dangling objects in .git/lost-found")),
|
||||
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(),
|
||||
};
|
||||
|
||||
@ -606,6 +629,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
include_reflogs = 0;
|
||||
}
|
||||
|
||||
if (name_objects)
|
||||
fsck_walk_options.object_names =
|
||||
xcalloc(1, sizeof(struct decoration));
|
||||
|
||||
git_config(fsck_config, NULL);
|
||||
|
||||
fsck_head_link();
|
||||
@ -661,6 +688,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
|
||||
obj->used = 1;
|
||||
if (name_objects)
|
||||
add_decoration(fsck_walk_options.object_names,
|
||||
obj, xstrdup(arg));
|
||||
mark_object_reachable(obj);
|
||||
heads++;
|
||||
continue;
|
||||
@ -693,6 +723,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
||||
continue;
|
||||
obj = &blob->object;
|
||||
obj->used = 1;
|
||||
if (name_objects)
|
||||
add_decoration(fsck_walk_options.object_names,
|
||||
obj,
|
||||
xstrfmt(":%s", active_cache[i]->name));
|
||||
mark_object_reachable(obj);
|
||||
}
|
||||
if (active_cache_tree)
|
||||
|
21
fsck.c
21
fsck.c
@ -323,6 +323,19 @@ static void put_object_name(struct fsck_options *options, struct object *obj,
|
||||
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)
|
||||
{
|
||||
struct tree_desc desc;
|
||||
@ -358,7 +371,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
|
||||
}
|
||||
else {
|
||||
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)
|
||||
return result;
|
||||
@ -454,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
|
||||
case OBJ_TAG:
|
||||
return fsck_walk_tag((struct tag *)obj, data, options);
|
||||
default:
|
||||
error("Unknown object type for %s", oid_to_hex(&obj->oid));
|
||||
error("Unknown object type for %s", describe_object(options, obj));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -901,9 +914,9 @@ int fsck_error_function(struct fsck_options *o,
|
||||
struct object *obj, int msg_type, const char *message)
|
||||
{
|
||||
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;
|
||||
}
|
||||
error("object %s: %s", oid_to_hex(&obj->oid), message);
|
||||
error("object %s: %s", describe_object(o, obj), message);
|
||||
return 1;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user