Merge branch 'ab/ambiguous-object-name'
Error output given in response to an ambiguous object name has been improved. * ab/ambiguous-object-name: object-name: re-use "struct strbuf" in show_ambiguous_object() object-name: iterate ambiguous objects before showing header object-name: show date for ambiguous tag objects object-name: make ambiguous object output translatable object-name: explicitly handle bad tags in show_ambiguous_object() object-name: explicitly handle OBJ_BAD in show_ambiguous_object() object-name tests: add tests for ambiguous object blind spots
This commit is contained in:
commit
268e6b8d4d
121
object-name.c
121
object-name.c
@ -351,35 +351,118 @@ static int init_object_disambiguation(struct repository *r,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ambiguous_output {
|
||||||
|
const struct disambiguate_state *ds;
|
||||||
|
struct strbuf advice;
|
||||||
|
struct strbuf sb;
|
||||||
|
};
|
||||||
|
|
||||||
static int show_ambiguous_object(const struct object_id *oid, void *data)
|
static int show_ambiguous_object(const struct object_id *oid, void *data)
|
||||||
{
|
{
|
||||||
const struct disambiguate_state *ds = data;
|
struct ambiguous_output *state = data;
|
||||||
struct strbuf desc = STRBUF_INIT;
|
const struct disambiguate_state *ds = state->ds;
|
||||||
|
struct strbuf *advice = &state->advice;
|
||||||
|
struct strbuf *sb = &state->sb;
|
||||||
int type;
|
int type;
|
||||||
|
const char *hash;
|
||||||
|
|
||||||
if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
|
if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
hash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);
|
||||||
type = oid_object_info(ds->repo, oid, NULL);
|
type = oid_object_info(ds->repo, oid, NULL);
|
||||||
|
|
||||||
|
if (type < 0) {
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous object
|
||||||
|
* output shown when we cannot look up or parse the
|
||||||
|
* object in question. E.g. "deadbeef [bad object]".
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s [bad object]"), hash);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(type == OBJ_TREE || type == OBJ_COMMIT ||
|
||||||
|
type == OBJ_BLOB || type == OBJ_TAG);
|
||||||
|
|
||||||
if (type == OBJ_COMMIT) {
|
if (type == OBJ_COMMIT) {
|
||||||
|
struct strbuf date = STRBUF_INIT;
|
||||||
|
struct strbuf msg = STRBUF_INIT;
|
||||||
struct commit *commit = lookup_commit(ds->repo, oid);
|
struct commit *commit = lookup_commit(ds->repo, oid);
|
||||||
|
|
||||||
if (commit) {
|
if (commit) {
|
||||||
struct pretty_print_context pp = {0};
|
struct pretty_print_context pp = {0};
|
||||||
pp.date_mode.type = DATE_SHORT;
|
pp.date_mode.type = DATE_SHORT;
|
||||||
format_commit_message(commit, " %ad - %s", &desc, &pp);
|
format_commit_message(commit, "%ad", &date, &pp);
|
||||||
|
format_commit_message(commit, "%s", &msg, &pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous commit
|
||||||
|
* object output. E.g.:
|
||||||
|
*
|
||||||
|
* "deadbeef commit 2021-01-01 - Some Commit Message"
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s commit %s - %s"), hash, date.buf,
|
||||||
|
msg.buf);
|
||||||
|
|
||||||
|
strbuf_release(&date);
|
||||||
|
strbuf_release(&msg);
|
||||||
} else if (type == OBJ_TAG) {
|
} else if (type == OBJ_TAG) {
|
||||||
struct tag *tag = lookup_tag(ds->repo, oid);
|
struct tag *tag = lookup_tag(ds->repo, oid);
|
||||||
if (!parse_tag(tag) && tag->tag)
|
|
||||||
strbuf_addf(&desc, " %s", tag->tag);
|
if (!parse_tag(tag) && tag->tag) {
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous
|
||||||
|
* tag object output. E.g.:
|
||||||
|
*
|
||||||
|
* "deadbeef tag 2022-01-01 - Some Tag Message"
|
||||||
|
*
|
||||||
|
* The second argument is the YYYY-MM-DD found
|
||||||
|
* in the tag.
|
||||||
|
*
|
||||||
|
* The third argument is the "tag" string
|
||||||
|
* from object.c.
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s tag %s - %s"), hash,
|
||||||
|
show_date(tag->date, 0, DATE_MODE(SHORT)),
|
||||||
|
tag->tag);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous
|
||||||
|
* tag object output where we couldn't parse
|
||||||
|
* the tag itself. E.g.:
|
||||||
|
*
|
||||||
|
* "deadbeef [bad tag, could not parse it]"
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s [bad tag, could not parse it]"),
|
||||||
|
hash);
|
||||||
|
}
|
||||||
|
} else if (type == OBJ_TREE) {
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous <type>
|
||||||
|
* object output. E.g. "deadbeef tree".
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s tree"), hash);
|
||||||
|
} else if (type == OBJ_BLOB) {
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is a line of ambiguous <type>
|
||||||
|
* object output. E.g. "deadbeef blob".
|
||||||
|
*/
|
||||||
|
strbuf_addf(sb, _("%s blob"), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
advise(" %s %s%s",
|
|
||||||
repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),
|
|
||||||
type_name(type) ? type_name(type) : "unknown type",
|
|
||||||
desc.buf);
|
|
||||||
|
|
||||||
strbuf_release(&desc);
|
out:
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: This is line item of ambiguous object output
|
||||||
|
* from describe_ambiguous_object() above. For RTL languages
|
||||||
|
* you'll probably want to swap the "%s" and leading " " space
|
||||||
|
* around.
|
||||||
|
*/
|
||||||
|
strbuf_addf(advice, _(" %s\n"), sb->buf);
|
||||||
|
|
||||||
|
strbuf_reset(sb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,6 +559,11 @@ static enum get_oid_result get_short_oid(struct repository *r,
|
|||||||
|
|
||||||
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
|
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
|
||||||
struct oid_array collect = OID_ARRAY_INIT;
|
struct oid_array collect = OID_ARRAY_INIT;
|
||||||
|
struct ambiguous_output out = {
|
||||||
|
.ds = &ds,
|
||||||
|
.sb = STRBUF_INIT,
|
||||||
|
.advice = STRBUF_INIT,
|
||||||
|
};
|
||||||
|
|
||||||
error(_("short object ID %s is ambiguous"), ds.hex_pfx);
|
error(_("short object ID %s is ambiguous"), ds.hex_pfx);
|
||||||
|
|
||||||
@ -488,13 +576,22 @@ static enum get_oid_result get_short_oid(struct repository *r,
|
|||||||
if (!ds.ambiguous)
|
if (!ds.ambiguous)
|
||||||
ds.fn = NULL;
|
ds.fn = NULL;
|
||||||
|
|
||||||
advise(_("The candidates are:"));
|
|
||||||
repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
|
repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
|
||||||
sort_ambiguous_oid_array(r, &collect);
|
sort_ambiguous_oid_array(r, &collect);
|
||||||
|
|
||||||
if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
|
if (oid_array_for_each(&collect, show_ambiguous_object, &out))
|
||||||
BUG("show_ambiguous_object shouldn't return non-zero");
|
BUG("show_ambiguous_object shouldn't return non-zero");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TRANSLATORS: The argument is the list of ambiguous
|
||||||
|
* objects composed in show_ambiguous_object(). See
|
||||||
|
* its "TRANSLATORS" comments for details.
|
||||||
|
*/
|
||||||
|
advise(_("The candidates are:\n%s"), out.advice.buf);
|
||||||
|
|
||||||
oid_array_clear(&collect);
|
oid_array_clear(&collect);
|
||||||
|
strbuf_release(&out.advice);
|
||||||
|
strbuf_release(&out.sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -25,6 +25,87 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
|||||||
|
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_cmp_failed_rev_parse () {
|
||||||
|
dir=$1
|
||||||
|
rev=$2
|
||||||
|
|
||||||
|
cat >expect &&
|
||||||
|
test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw &&
|
||||||
|
sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'ambiguous blob output' '
|
||||||
|
git init --bare blob.prefix &&
|
||||||
|
(
|
||||||
|
cd blob.prefix &&
|
||||||
|
|
||||||
|
# Both start with "dead..", under both SHA-1 and SHA-256
|
||||||
|
echo brocdnra | git hash-object -w --stdin &&
|
||||||
|
echo brigddsv | git hash-object -w --stdin &&
|
||||||
|
|
||||||
|
# Both start with "beef.."
|
||||||
|
echo 1agllotbh | git hash-object -w --stdin &&
|
||||||
|
echo 1bbfctrkc | git hash-object -w --stdin
|
||||||
|
) &&
|
||||||
|
|
||||||
|
test_must_fail git -C blob.prefix rev-parse dead &&
|
||||||
|
test_cmp_failed_rev_parse blob.prefix beef <<-\EOF
|
||||||
|
error: short object ID beef... is ambiguous
|
||||||
|
hint: The candidates are:
|
||||||
|
hint: beef... blob
|
||||||
|
hint: beef... blob
|
||||||
|
fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree.
|
||||||
|
Use '\''--'\'' to separate paths from revisions, like this:
|
||||||
|
'\''git <command> [<revision>...] -- [<file>...]'\''
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '
|
||||||
|
git init --bare blob.bad &&
|
||||||
|
(
|
||||||
|
cd blob.bad &&
|
||||||
|
|
||||||
|
# Both have the prefix "bad0"
|
||||||
|
echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&
|
||||||
|
echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally
|
||||||
|
) &&
|
||||||
|
|
||||||
|
test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF
|
||||||
|
error: short object ID bad0... is ambiguous
|
||||||
|
fatal: invalid object type
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '
|
||||||
|
git init --bare blob.corrupt &&
|
||||||
|
(
|
||||||
|
cd blob.corrupt &&
|
||||||
|
|
||||||
|
# Both have the prefix "cafe"
|
||||||
|
echo bnkxmdwz | git hash-object -w --stdin &&
|
||||||
|
oid=$(echo bmwsjxzi | git hash-object -w --stdin) &&
|
||||||
|
|
||||||
|
oidf=objects/$(test_oid_to_path "$oid") &&
|
||||||
|
chmod 755 $oidf &&
|
||||||
|
echo broken >$oidf
|
||||||
|
) &&
|
||||||
|
|
||||||
|
test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF
|
||||||
|
error: short object ID cafe... is ambiguous
|
||||||
|
error: inflate: data stream error (incorrect header check)
|
||||||
|
error: unable to unpack cafe... header
|
||||||
|
error: inflate: data stream error (incorrect header check)
|
||||||
|
error: unable to unpack cafe... header
|
||||||
|
hint: The candidates are:
|
||||||
|
hint: cafe... [bad object]
|
||||||
|
hint: cafe... blob
|
||||||
|
fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree.
|
||||||
|
Use '\''--'\'' to separate paths from revisions, like this:
|
||||||
|
'\''git <command> [<revision>...] -- [<file>...]'\''
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
if ! test_have_prereq SHA1
|
if ! test_have_prereq SHA1
|
||||||
then
|
then
|
||||||
skip_all='not using SHA-1 for objects'
|
skip_all='not using SHA-1 for objects'
|
||||||
|
Loading…
Reference in New Issue
Block a user