]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ab/ambiguous-object-name'
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Feb 2022 23:47:33 +0000 (15:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 25 Feb 2022 23:47:33 +0000 (15:47 -0800)
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

object-name.c
t/t1512-rev-parse-disambiguation.sh

index 92862eeb1ac74f0a3eeaca91ffac9771331bbf22..0230a87416666cc96575002236a6be1f172beb8a 100644 (file)
@@ -351,35 +351,118 @@ static int init_object_disambiguation(struct repository *r,
        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)
 {
-       const struct disambiguate_state *ds = data;
-       struct strbuf desc = STRBUF_INIT;
+       struct ambiguous_output *state = data;
+       const struct disambiguate_state *ds = state->ds;
+       struct strbuf *advice = &state->advice;
+       struct strbuf *sb = &state->sb;
        int type;
+       const char *hash;
 
        if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
                return 0;
 
+       hash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);
        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) {
+               struct strbuf date = STRBUF_INIT;
+               struct strbuf msg = STRBUF_INIT;
                struct commit *commit = lookup_commit(ds->repo, oid);
+
                if (commit) {
                        struct pretty_print_context pp = {0};
                        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) {
                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;
 }
 
@@ -476,6 +559,11 @@ static enum get_oid_result get_short_oid(struct repository *r,
 
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
                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);
 
@@ -488,13 +576,22 @@ static enum get_oid_result get_short_oid(struct repository *r,
                if (!ds.ambiguous)
                        ds.fn = NULL;
 
-               advise(_("The candidates are:"));
                repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &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");
+
+               /*
+                * 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);
+               strbuf_release(&out.advice);
+               strbuf_release(&out.sb);
        }
 
        return status;
index b0119bf8bc8417439bee91bb7c1add21bc460c2e..98cefe3b7039fe726148e47fdf81b1f9fb454e15 100755 (executable)
@@ -25,6 +25,87 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./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
 then
        skip_all='not using SHA-1 for objects'