{"thread":{"id":"56641","subject":"[PATCH 0/2] i18n: improve translatability of ambiguous object output","startedAt":"2021-10-04T01:43:24Z","lastAt":"2022-01-27T20:14:11Z","messageCount":90,"participants":["Ævar Arnfjörð Bjarmason","Eric Sunshine","Jeff King","Bagas Sanjaya","Junio C Hamano","Josh Steadmon","René Scharfe","Johannes Altmanninger"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"437839","messageId":"cover-0.2-00000000000-20211004T013611Z-avarab@gmail.com","threadId":"56641","inReplyTo":null,"subject":"[PATCH 0/2] i18n: improve translatability of ambiguous object output","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T01:42:47Z","receivedAt":"2021-10-04T01:43:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series improves the translatability of the output we emit when an\nambiguous OID is given by not emitting it a line-at-a-time. This\nlikely won't matter in practice except for RTL languages (of which we\nhave no current translations), but it's good to be future-proof!\n\nÆvar Arnfjörð Bjarmason (2):\n  object-name tests: tighten up advise() output test\n  object-name: make ambiguous object output translatable\n\n object-name.c                       | 53 ++++++++++++++++++++++++-----\n t/t1512-rev-parse-disambiguation.sh | 16 ++++-----\n 2 files changed, 52 insertions(+), 17 deletions(-)\n\n-- \n2.33.0.1404.g7bcfc82b295\n\n"},{"id":"437840","messageId":"patch-1.2-7085f951a12-20211004T013611Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-0.2-00000000000-20211004T013611Z-avarab@gmail.com","subject":"[PATCH 1/2] object-name tests: tighten up advise() output test","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T01:42:48Z","receivedAt":"2021-10-04T01:43:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change tests added in 1ffa26c4614 (get_short_sha1: list ambiguous\nobjects on error, 2016-09-26) to only care about the OIDs that are\nlisted, which is what the test is trying to check for.\n\nThis isn't needed by the subsequent commit, which won't change any of\nthe output, but a mere tightening of the tests assertions to more\nclosely match what we really want to test for here.\n\nNow if the advise() message itself were change the phrasing around the\nlist of OIDs we won't have this test break. We're assuming that such\noutput won't have a need to indent anything except the OIDs.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1512-rev-parse-disambiguation.sh | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 7891a6becf3..d3a2d9188c7 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -334,16 +334,16 @@ test_expect_success 'ambiguity errors are not repeated (peel)' '\n \n test_expect_success 'ambiguity hints' '\n \ttest_must_fail git rev-parse 000000000 2>stderr &&\n-\tgrep ^hint: stderr >hints &&\n-\t# 16 candidates, plus one intro line\n-\ttest_line_count = 17 hints\n+\tgrep \"^hint:   \" stderr >hints &&\n+\t# 16 candidates, minus surrounding prose\n+\ttest_line_count = 16 hints\n '\n \n test_expect_success 'ambiguity hints respect type' '\n \ttest_must_fail git rev-parse 000000000^{commit} 2>stderr &&\n-\tgrep ^hint: stderr >hints &&\n-\t# 5 commits, 1 tag (which is a committish), plus intro line\n-\ttest_line_count = 7 hints\n+\tgrep \"^hint:   \" stderr >hints &&\n+\t# 5 commits, 1 tag (which is a committish), minus surrounding prose\n+\ttest_line_count = 6 hints\n '\n \n test_expect_success 'failed type-selector still shows hint' '\n@@ -352,8 +352,8 @@ test_expect_success 'failed type-selector still shows hint' '\n \techo 851 | git hash-object --stdin -w &&\n \techo 872 | git hash-object --stdin -w &&\n \ttest_must_fail git rev-parse ee3d^{commit} 2>stderr &&\n-\tgrep ^hint: stderr >hints &&\n-\ttest_line_count = 3 hints\n+\tgrep \"^hint:   \" stderr >hints &&\n+\ttest_line_count = 2 hints\n '\n \n test_expect_success 'core.disambiguate config can prefer types' '\n-- \n2.33.0.1404.g7bcfc82b295\n\n"},{"id":"437841","messageId":"patch-2.2-b6136380c28-20211004T013611Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-0.2-00000000000-20211004T013611Z-avarab@gmail.com","subject":"[PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T01:42:49Z","receivedAt":"2021-10-04T01:45:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] to be more friendly to translators. By being able to\ncustomize the sprintf formats we're even ready for RTL languages.\n\n1. ef9b0370da6 (sha1-name.c: store and use repo in struct\n   disambiguate_state, 2019-04-16)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 53 ++++++++++++++++++++++++++++++++++++++++++---------\n 1 file changed, 44 insertions(+), 9 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..7e7f671e337 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -351,9 +351,16 @@ static int init_object_disambiguation(struct repository *r,\n \treturn 0;\n }\n \n+struct show_ambiguous_state {\n+\tconst struct disambiguate_state *ds;\n+\tstruct strbuf *advice;\n+};\n+\n static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n-\tconst struct disambiguate_state *ds = data;\n+\tstruct show_ambiguous_state *state = data;\n+\tconst struct disambiguate_state *ds = state->ds;\n+\tstruct strbuf *advice = state->advice;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n \n@@ -366,18 +373,34 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, _(\" %ad - %s\"), &desc, &pp);\n \t\t}\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\tstrbuf_addf(&desc, _(\" %s\"), tag->tag);\n \t}\n \n-\tadvise(\"  %s %s%s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n-\t       desc.buf);\n+\tstrbuf_addf(advice,\n+\t\t    /*\n+\t\t     * TRANSLATORS: This is a line of ambiguous object\n+\t\t     * output. E.g.:\n+\t\t     *\n+\t\t     *    \"deadbeef commit 2021-01-01 - Some Commit Message\\n\"\n+\t\t     *    \"deadbeef tag Some Tag Message\\n\"\n+\t\t     *    \"deadbeef tree\\n\"\n+\t\t     *\n+\t\t     * I.e. the first argument is a short OID, the\n+\t\t     * second is the type name of the object, and the\n+\t\t     * third a description of the object, if it's a\n+\t\t     * commit or tag. In that case the \" %ad - %s\" and\n+\t\t     * \" %s\" formats above will be used for the third\n+\t\t     * argument.\n+\t\t     */\n+\t\t    _(\"  %s %s%s\\n\"),\n+\t\t    repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n+\t\t    type_name(type) ? type_name(type) : \"unknown type\",\n+\t\t    desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n@@ -475,7 +498,12 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t}\n \n \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n+\t\tstruct show_ambiguous_state as = {\n+\t\t\t.ds = &ds,\n+\t\t\t.advice = &sb,\n+\t\t};\n \n \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n \n@@ -488,12 +516,19 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tif (!ds.ambiguous)\n \t\t\tds.fn = NULL;\n \n-\t\tadvise(_(\"The candidates are:\"));\n \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n \t\tsort_ambiguous_oid_array(r, &collect);\n \n-\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &as))\n \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n+\n+\t\t/*\n+\t\t * TRANSLATORS: The argument is the list of ambiguous\n+\t\t * objects composed in show_ambiguous_object(). See\n+\t\t * its \"TRANSLATORS\" comment for details.\n+\t\t */\n+\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n+\n \t\toid_array_clear(&collect);\n \t}\n \n-- \n2.33.0.1404.g7bcfc82b295\n\n"},{"id":"437848","messageId":"CAPig+cSnMe5M43vm2TjKayTE+PLJ_-m3Qmb6B=W_N1-fh2i5Ug@mail.gmail.com","threadId":"56641","inReplyTo":"patch-1.2-7085f951a12-20211004T013611Z-avarab@gmail.com","subject":"Re: [PATCH 1/2] object-name tests: tighten up advise() output test","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-10-04T02:52:51Z","receivedAt":"2021-10-04T02:53:09Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Oct 3, 2021 at 9:43 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> Change tests added in 1ffa26c4614 (get_short_sha1: list ambiguous\n> objects on error, 2016-09-26) to only care about the OIDs that are\n> listed, which is what the test is trying to check for.\n>\n> This isn't needed by the subsequent commit, which won't change any of\n> the output, but a mere tightening of the tests assertions to more\n> closely match what we really want to test for here.\n>\n> Now if the advise() message itself were change the phrasing around the\n\ns/were change/were to change/\n\n> list of OIDs we won't have this test break. We're assuming that such\n> output won't have a need to indent anything except the OIDs.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"437851","messageId":"YVqnpNQf3P5+WOMG@coredump.intra.peff.net","threadId":"56641","inReplyTo":"patch-1.2-7085f951a12-20211004T013611Z-avarab@gmail.com","subject":"Re: [PATCH 1/2] object-name tests: tighten up advise() output test","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-04T07:05:08Z","receivedAt":"2021-10-04T07:05:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 03:42:48AM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Change tests added in 1ffa26c4614 (get_short_sha1: list ambiguous\n> objects on error, 2016-09-26) to only care about the OIDs that are\n> listed, which is what the test is trying to check for.\n> \n> This isn't needed by the subsequent commit, which won't change any of\n> the output, but a mere tightening of the tests assertions to more\n> closely match what we really want to test for here.\n\nI think the next commit does change the output. It adds an extra empty\nline which would cause these tests to fail.\n\n> Now if the advise() message itself were change the phrasing around the\n> list of OIDs we won't have this test break. We're assuming that such\n> output won't have a need to indent anything except the OIDs.\n\nIt feels like we're trading one assumption for another. :)\n\nI admit that don't care much either way, though.\n\n-Peff\n"},{"id":"437856","messageId":"YVqu0aEBMy3mnYoE@coredump.intra.peff.net","threadId":"56641","inReplyTo":"patch-2.2-b6136380c28-20211004T013611Z-avarab@gmail.com","subject":"Re: [PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-04T07:35:45Z","receivedAt":"2021-10-04T07:35:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 03:42:49AM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Change the output of show_ambiguous_object() added in [1] and last\n> tweaked in [2] to be more friendly to translators. By being able to\n> customize the sprintf formats we're even ready for RTL languages.\n> \n> 1. ef9b0370da6 (sha1-name.c: store and use repo in struct\n>    disambiguate_state, 2019-04-16)\n> 2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n>    then SHA-1, 2018-05-10)\n\nI suspect you meant 1ffa26c461 (get_short_sha1: list ambiguous objects\non error, 2016-09-26) for the first one.\n\nI had to stare at the patch for a while to understand the goal here. I\nthink this would have been a bit easier to review if \"change\" in your\nfirst sentence was described a bit more. Perhaps:\n\n  The list of candidates output by show_ambiguous_output() is not marked\n  for translation. At the very least we want to allow the text \"the\n  candidates are\" to be translated. But we also format individual\n  candidate lines like:\n\n      deadbeef commit 2021-01-01 - Some Commit Message\n\n  by formatting the individual components, then using a printf-format to\n  arrange them in the correct order. Even though there's no text here to\n  be translated, the order and spacing is determined by the format\n  string. Allowing that to be translated helps RTL languages.\n\nI have a few comments on the patch itself. The biggest thing is that it\nchanges the format to add an extra newline (between \"The candidates\nare:\" and the actual list). I don't have a strong opinion on including\nthat or not, but it seemed unintentional given the comment on the first\ncommit (and its lack of mention here).\n\nThe rest are mostly observations, not criticisms. You can take them with\nthe appropriate grain of salt given that I don't do translation work\nmyself, nor know any RTL languages.\n\n> @@ -366,18 +373,34 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t\tif (commit) {\n>  \t\t\tstruct pretty_print_context pp = {0};\n>  \t\t\tpp.date_mode.type = DATE_SHORT;\n> -\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n> +\t\t\tformat_commit_message(commit, _(\" %ad - %s\"), &desc, &pp);\n>  \t\t}\n\nIs it OK to use non-printf expansions with the gettext code? Presumably\nthe translated string would have the same set of placeholders in it, but\nmy understanding is that gettext may sometimes munge the %-placeholders\n(e.g., allowing numbered ones for re-ordering). I admit I don't know how\nany of that works, but I just wonder if this \"%ad\" may cause confusion\n(or even if not, if it is even possible to re-order it for an RTL\nlanguage).\n\n>  \t} else if (type == OBJ_TAG) {\n>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>  \t\tif (!parse_tag(tag) && tag->tag)\n> -\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n> +\t\t\tstrbuf_addf(&desc, _(\" %s\"), tag->tag);\n>  \t}\n\nI wonder whether \" %s\" is worthwhile as a translatable string. It does\nseem to be unique among strings marked for translation, but there are a\nton of non-translated instances. Would context ever matter here?\n\nMy impression is that this kind of translation-lego is frowned upon, and\nwe might be better off repeating ourselves a bit more. I.e., something\nlike:\n\n  if (commit) {\n\t  struct strbuf date = STRBUF_INIT;\n\t  struct strbuf subject = STRBUF_INIT;\n\t  format_commit_message(commit, \"%ad\", &date, &pp);\n\t  format_commit_message(commit, \"%s\", &subject, &pp);\n\t  strbuf_addf(advice, _(\"  %s commit %s - %s\\n\"),\n\t\t      repo_find_unique_abbrev(...),\n\t\t      date.buf, subject.buf);\n\t  strbuf_release(&date);\n\t  strbuf_release(&subject);\n  } else if (type == OBJ_TAG) {\n          ...\n\t  strbuf_addf(advice, _(\"  %s tag %s\\n\"),\n\t              repo_find_unique_abbrev(...), tag->tag);\n  } else {\n\t  /* TRANSLATORS: the fields are abbreviated oid and type */\n          strbuf_addf(advice, _(\"  %s %s\\n\"),\n\t              repo_find_unique_abbrev(...), type_name(type));\n  }\n\nThough that last one similarly has a real lack of context.\n\n> -\tadvise(\"  %s %s%s\",\n> -\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n> -\t       type_name(type) ? type_name(type) : \"unknown type\",\n> -\t       desc.buf);\n> +\tstrbuf_addf(advice,\n> +\t\t    /*\n> +\t\t     * TRANSLATORS: This is a line of ambiguous object\n> +\t\t     * output. E.g.:\n> +\t\t     *\n> +\t\t     *    \"deadbeef commit 2021-01-01 - Some Commit Message\\n\"\n> +\t\t     *    \"deadbeef tag Some Tag Message\\n\"\n> +\t\t     *    \"deadbeef tree\\n\"\n> +\t\t     *\n> +\t\t     * I.e. the first argument is a short OID, the\n> +\t\t     * second is the type name of the object, and the\n> +\t\t     * third a description of the object, if it's a\n> +\t\t     * commit or tag. In that case the \" %ad - %s\" and\n> +\t\t     * \" %s\" formats above will be used for the third\n> +\t\t     * argument.\n> +\t\t     */\n> +\t\t    _(\"  %s %s%s\\n\"),\n> +\t\t    repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n> +\t\t    type_name(type) ? type_name(type) : \"unknown type\",\n> +\t\t    desc.buf);\n\nWould you want to translate \"unknown type\" here, as well? It's probably\nnot that important in practice, but it seems like a funny omission.\n\n> @@ -488,12 +516,19 @@ static enum get_oid_result get_short_oid(struct repository *r,\n>  \t\tif (!ds.ambiguous)\n>  \t\t\tds.fn = NULL;\n>  \n> -\t\tadvise(_(\"The candidates are:\"));\n>  \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n>  \t\tsort_ambiguous_oid_array(r, &collect);\n>  \n> -\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n> +\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &as))\n>  \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n> +\n> +\t\t/*\n> +\t\t * TRANSLATORS: The argument is the list of ambiguous\n> +\t\t * objects composed in show_ambiguous_object(). See\n> +\t\t * its \"TRANSLATORS\" comment for details.\n> +\t\t */\n> +\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n\nHere's where the extra newline.\n\nI understand why the earlier ones were changed for RTL languages. But\nthis one is always line-oriented. Is the point to help bottom-to-top\nlanguages? I can buy that, though it feels like that would be something\nthat the terminal would deal with (because even with this, you're still\ngetting the \"error:\" line printed separately, for example).\n\nI don't think what this is doing is wrong (at first I wondered about the\n\"hint:\" lines, but because advise() looks for embedded newlines, we're\nOK). But if the translation doesn't need to reorder things across lines,\nthis extra format-into-a-strbuf step doesn't seem necessary. We can just\ncall advise() directly in show_ambiguous_object(), as before.\n\nIf it is necessary, then note that you leak \"sb\" here.\n\n-Peff\n"},{"id":"437862","messageId":"87o885nq4z.fsf@evledraar.gmail.com","threadId":"56641","inReplyTo":"YVqu0aEBMy3mnYoE@coredump.intra.peff.net","subject":"Re: [PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T08:26:10Z","receivedAt":"2021-10-04T08:36:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Oct 04 2021, Jeff King wrote:\n\n> On Mon, Oct 04, 2021 at 03:42:49AM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Change the output of show_ambiguous_object() added in [1] and last\n>> tweaked in [2] to be more friendly to translators. By being able to\n>> customize the sprintf formats we're even ready for RTL languages.\n>> \n>> 1. ef9b0370da6 (sha1-name.c: store and use repo in struct\n>>    disambiguate_state, 2019-04-16)\n>> 2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n>>    then SHA-1, 2018-05-10)\n>\n> I suspect you meant 1ffa26c461 (get_short_sha1: list ambiguous objects\n> on error, 2016-09-26) for the first one.\n>\n> I had to stare at the patch for a while to understand the goal here. I\n> think this would have been a bit easier to review if \"change\" in your\n> first sentence was described a bit more. Perhaps:\n>\n>   The list of candidates output by show_ambiguous_output() is not marked\n>   for translation. At the very least we want to allow the text \"the\n>   candidates are\" to be translated. But we also format individual\n>   candidate lines like:\n>\n>       deadbeef commit 2021-01-01 - Some Commit Message\n>\n>   by formatting the individual components, then using a printf-format to\n>   arrange them in the correct order. Even though there's no text here to\n>   be translated, the order and spacing is determined by the format\n>   string. Allowing that to be translated helps RTL languages.\n>\n> I have a few comments on the patch itself. The biggest thing is that it\n> changes the format to add an extra newline (between \"The candidates\n> are:\" and the actual list). I don't have a strong opinion on including\n> that or not, but it seemed unintentional given the comment on the first\n> commit (and its lack of mention here).\n\nThat was unintentional, sorry. Will fix.\n\n> The rest are mostly observations, not criticisms. You can take them with\n> the appropriate grain of salt given that I don't do translation work\n> myself, nor know any RTL languages.\n>\n>> @@ -366,18 +373,34 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>>  \t\tif (commit) {\n>>  \t\t\tstruct pretty_print_context pp = {0};\n>>  \t\t\tpp.date_mode.type = DATE_SHORT;\n>> -\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n>> +\t\t\tformat_commit_message(commit, _(\" %ad - %s\"), &desc, &pp);\n>>  \t\t}\n>\n> Is it OK to use non-printf expansions with the gettext code? Presumably\n> the translated string would have the same set of placeholders in it, but\n> my understanding is that gettext may sometimes munge the %-placeholders\n> (e.g., allowing numbered ones for re-ordering). I admit I don't know how\n> any of that works, but I just wonder if this \"%ad\" may cause confusion\n> (or even if not, if it is even possible to re-order it for an RTL\n> language).\n\nIt's not, oops. I missed that, blinders on for the \"%ad\". Will construct\nit in advance and use %s interpolation separately.\n\n>>  \t} else if (type == OBJ_TAG) {\n>>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>>  \t\tif (!parse_tag(tag) && tag->tag)\n>> -\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n>> +\t\t\tstrbuf_addf(&desc, _(\" %s\"), tag->tag);\n>>  \t}\n>\n> I wonder whether \" %s\" is worthwhile as a translatable string. It does\n> seem to be unique among strings marked for translation, but there are a\n> ton of non-translated instances. Would context ever matter here?\n>\n> My impression is that this kind of translation-lego is frowned upon, and\n> we might be better off repeating ourselves a bit more. I.e., something\n> like:\n>\n>   if (commit) {\n> \t  struct strbuf date = STRBUF_INIT;\n> \t  struct strbuf subject = STRBUF_INIT;\n> \t  format_commit_message(commit, \"%ad\", &date, &pp);\n> \t  format_commit_message(commit, \"%s\", &subject, &pp);\n> \t  strbuf_addf(advice, _(\"  %s commit %s - %s\\n\"),\n> \t\t      repo_find_unique_abbrev(...),\n> \t\t      date.buf, subject.buf);\n> \t  strbuf_release(&date);\n> \t  strbuf_release(&subject);\n>   } else if (type == OBJ_TAG) {\n>           ...\n> \t  strbuf_addf(advice, _(\"  %s tag %s\\n\"),\n> \t              repo_find_unique_abbrev(...), tag->tag);\n>   } else {\n> \t  /* TRANSLATORS: the fields are abbreviated oid and type */\n>           strbuf_addf(advice, _(\"  %s %s\\n\"),\n> \t              repo_find_unique_abbrev(...), type_name(type));\n>   }\n>\n> Though that last one similarly has a real lack of context.\n\nYeah that's better. Will change it to something like that.\n\n>> -\tadvise(\"  %s %s%s\",\n>> -\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n>> -\t       type_name(type) ? type_name(type) : \"unknown type\",\n>> -\t       desc.buf);\n>> +\tstrbuf_addf(advice,\n>> +\t\t    /*\n>> +\t\t     * TRANSLATORS: This is a line of ambiguous object\n>> +\t\t     * output. E.g.:\n>> +\t\t     *\n>> +\t\t     *    \"deadbeef commit 2021-01-01 - Some Commit Message\\n\"\n>> +\t\t     *    \"deadbeef tag Some Tag Message\\n\"\n>> +\t\t     *    \"deadbeef tree\\n\"\n>> +\t\t     *\n>> +\t\t     * I.e. the first argument is a short OID, the\n>> +\t\t     * second is the type name of the object, and the\n>> +\t\t     * third a description of the object, if it's a\n>> +\t\t     * commit or tag. In that case the \" %ad - %s\" and\n>> +\t\t     * \" %s\" formats above will be used for the third\n>> +\t\t     * argument.\n>> +\t\t     */\n>> +\t\t    _(\"  %s %s%s\\n\"),\n>> +\t\t    repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n>> +\t\t    type_name(type) ? type_name(type) : \"unknown type\",\n>> +\t\t    desc.buf);\n>\n> Would you want to translate \"unknown type\" here, as well? It's probably\n> not that important in practice, but it seems like a funny omission.\n\nWilldo.\n\n>> @@ -488,12 +516,19 @@ static enum get_oid_result get_short_oid(struct repository *r,\n>>  \t\tif (!ds.ambiguous)\n>>  \t\t\tds.fn = NULL;\n>>  \n>> -\t\tadvise(_(\"The candidates are:\"));\n>>  \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n>>  \t\tsort_ambiguous_oid_array(r, &collect);\n>>  \n>> -\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n>> +\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &as))\n>>  \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n>> +\n>> +\t\t/*\n>> +\t\t * TRANSLATORS: The argument is the list of ambiguous\n>> +\t\t * objects composed in show_ambiguous_object(). See\n>> +\t\t * its \"TRANSLATORS\" comment for details.\n>> +\t\t */\n>> +\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n>\n> Here's where the extra newline.\n>\n> I understand why the earlier ones were changed for RTL languages. But\n> this one is always line-oriented. Is the point to help bottom-to-top\n> languages? I can buy that, though it feels like that would be something\n> that the terminal would deal with (because even with this, you're still\n> getting the \"error:\" line printed separately, for example).\n>\n> I don't think what this is doing is wrong (at first I wondered about the\n> \"hint:\" lines, but because advise() looks for embedded newlines, we're\n> OK). But if the translation doesn't need to reorder things across lines,\n> this extra format-into-a-strbuf step doesn't seem necessary. We can just\n> call advise() directly in show_ambiguous_object(), as before.\n>\n> If it is necessary, then note that you leak \"sb\" here.\n\nI'll keep that bit as-is, it's not strictly necessary, but it gives\ntranslators a bit more context.\n"},{"id":"437864","messageId":"YVrJj0Ltuc1Tcm7t@coredump.intra.peff.net","threadId":"56641","inReplyTo":"87o885nq4z.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-04T09:29:51Z","receivedAt":"2021-10-04T09:29:56Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 10:26:10AM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> >> +\t\t/*\n> >> +\t\t * TRANSLATORS: The argument is the list of ambiguous\n> >> +\t\t * objects composed in show_ambiguous_object(). See\n> >> +\t\t * its \"TRANSLATORS\" comment for details.\n> >> +\t\t */\n> >> +\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n> >\n> > Here's where the extra newline.\n> >\n> > I understand why the earlier ones were changed for RTL languages. But\n> > this one is always line-oriented. Is the point to help bottom-to-top\n> > languages? I can buy that, though it feels like that would be something\n> > that the terminal would deal with (because even with this, you're still\n> > getting the \"error:\" line printed separately, for example).\n> >\n> > I don't think what this is doing is wrong (at first I wondered about the\n> > \"hint:\" lines, but because advise() looks for embedded newlines, we're\n> > OK). But if the translation doesn't need to reorder things across lines,\n> > this extra format-into-a-strbuf step doesn't seem necessary. We can just\n> > call advise() directly in show_ambiguous_object(), as before.\n> >\n> > If it is necessary, then note that you leak \"sb\" here.\n> \n> I'll keep that bit as-is, it's not strictly necessary, but it gives\n> translators a bit more context.\n\nIf it's just for the context, wouldn't this do the same thing:\n\n  /*\n   * TRANSLATORS: This is followed by the list of ambiguous\n   * objects composed in show_ambiguous_object(). See its\n   * \"TRANSLATORS\" comments for details.\n   */\n  advise(_(\"The candidates are:\"));\n  ...\n  if (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n     ...\n\nI.e., leave the code as-is, and just add the extra comment. There is no\nneed for the extra struct or any change of ordering between this\nadvise() and the others.\n\nI would think it is worthwhile if we are de-lego-ing a message that is\nmade in chunks, but in this case the we have to construct an opaque \"%s\"\nto represent the individual lines for each object, because we don't know\nhow many of them there will be.\n\n-Peff\n\nPS In my \"something like this\" commit message, I indicated that the\n   \"candidates\" message was getting translated, but it actually is\n   already translated in the pre-image. So I think we would not need to\n   touch that line at all.\n"},{"id":"437876","messageId":"877detni03.fsf@evledraar.gmail.com","threadId":"56641","inReplyTo":"YVrJj0Ltuc1Tcm7t@coredump.intra.peff.net","subject":"Re: [PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T11:16:24Z","receivedAt":"2021-10-04T11:31:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Oct 04 2021, Jeff King wrote:\n\n> On Mon, Oct 04, 2021 at 10:26:10AM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> >> +\t\t/*\n>> >> +\t\t * TRANSLATORS: The argument is the list of ambiguous\n>> >> +\t\t * objects composed in show_ambiguous_object(). See\n>> >> +\t\t * its \"TRANSLATORS\" comment for details.\n>> >> +\t\t */\n>> >> +\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n>> >\n>> > Here's where the extra newline.\n>> >\n>> > I understand why the earlier ones were changed for RTL languages. But\n>> > this one is always line-oriented. Is the point to help bottom-to-top\n>> > languages? I can buy that, though it feels like that would be something\n>> > that the terminal would deal with (because even with this, you're still\n>> > getting the \"error:\" line printed separately, for example).\n>> >\n>> > I don't think what this is doing is wrong (at first I wondered about the\n>> > \"hint:\" lines, but because advise() looks for embedded newlines, we're\n>> > OK). But if the translation doesn't need to reorder things across lines,\n>> > this extra format-into-a-strbuf step doesn't seem necessary. We can just\n>> > call advise() directly in show_ambiguous_object(), as before.\n>> >\n>> > If it is necessary, then note that you leak \"sb\" here.\n>> \n>> I'll keep that bit as-is, it's not strictly necessary, but it gives\n>> translators a bit more context.\n>\n> If it's just for the context, wouldn't this do the same thing:\n>\n>   /*\n>    * TRANSLATORS: This is followed by the list of ambiguous\n>    * objects composed in show_ambiguous_object(). See its\n>    * \"TRANSLATORS\" comments for details.\n>    */\n>   advise(_(\"The candidates are:\"));\n>   ...\n>   if (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n>      ...\n>\n> I.e., leave the code as-is, and just add the extra comment. There is no\n> need for the extra struct or any change of ordering between this\n> advise() and the others.\n>\n> I would think it is worthwhile if we are de-lego-ing a message that is\n> made in chunks, but in this case the we have to construct an opaque \"%s\"\n> to represent the individual lines for each object, because we don't know\n> how many of them there will be.\n>\n> -Peff\n>\n> PS In my \"something like this\" commit message, I indicated that the\n>    \"candidates\" message was getting translated, but it actually is\n>    already translated in the pre-image. So I think we would not need to\n>    touch that line at all.\n\nYes you're right. You've got me, I guess :)\n\nAn unstated motivation of mine here is that I've got a series that\nchanges the advise() function itself so that it automatically adds the\n\"and run xyz to disable this message\".\n\nNow some don't emit it, some don't even have associated configuration or\ndocumentation. It's a mess.\n\nI originally hacked this up because this is the one in-tree user of\nadvise() that constructs output incrementally. So for that improvement\nto advise() it either needs to be changed to not do so (this patch), or\nI'd need an advise_no_template() or advise_hint_line() or whatever as a\nworkaround.\n\nI didn't mean to be too subterfuge-y about it. It's just hard to find a\nbalance between a single long series & a few shorter ones, and when to\ndistract reviewers with \"this design choice is also because of XYZ\ntangentally related end-goal\".\n\nAnyway, now that we're here I'm not sure what the best way forward\nis. One is to just address the pointed-out bugs and keep that\naccumulate/print pattern I instituded, which would help that subsequent\nseries. But I agree that while I think it is a bit better to translate\nthe \"foo:\\n\\n%s\" message (it gives a bit more context about what sort of\nmessage it is), it's not really worth it just in the context of this\npatch.\n\nWhat do you think? That we could let this pass for now, or we should\ndrop this and I can try to re-visit it as part of some larger topic?\nThat meaningful improvement to advise() depends on this + another series\nof advise fixes I submitted in parallel at [1].\n\n1. https://lore.kernel.org/git/cover-0.5-00000000000-20211004T015432Z-avarab@gmail.com/T/#t\n"},{"id":"437878","messageId":"YVrudGOcUxblsfPY@coredump.intra.peff.net","threadId":"56641","inReplyTo":"877detni03.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] object-name: make ambiguous object output translatable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-04T12:07:16Z","receivedAt":"2021-10-04T12:07:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 01:16:24PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> An unstated motivation of mine here is that I've got a series that\n> changes the advise() function itself so that it automatically adds the\n> \"and run xyz to disable this message\".\n> \n> Now some don't emit it, some don't even have associated configuration or\n> documentation. It's a mess.\n> \n> I originally hacked this up because this is the one in-tree user of\n> advise() that constructs output incrementally. So for that improvement\n> to advise() it either needs to be changed to not do so (this patch), or\n> I'd need an advise_no_template() or advise_hint_line() or whatever as a\n> workaround.\n\nOK, that makes sense. In general, I think it's much easier if those\nmotivations _aren't_ unstated. Both for the benefit of reviewers, but\nalso folks reading commit messages later who wonder \"hey, it looks like\nwe didn't need this hunk, and it is causing a hassle, so why can't I\njust revert it\".\n\nBut...\n\n> I didn't mean to be too subterfuge-y about it. It's just hard to find a\n> balance between a single long series & a few shorter ones, and when to\n> distract reviewers with \"this design choice is also because of XYZ\n> tangentally related end-goal\".\n\n...yeah, if you have patches that say \"do X, because later maybe we'll\ndo Y\", then it is often hard to evaluate them if Y is not in the same\nseries.  And _especially_ so if there is some other Z happening in the\ncurrent series with X, because even talking about X is muddling things.\n\nSo in an ideal world, you'd not do X at all (in this case, touch the\nadvise() lines), and leave Y (rolling up a buf to hand to a single\nadvise() line) as a preparatory patch in a series that does Z (your\nchange to advise() to print the extra stuff).\n\nThings don't always break down that way, but I think they do here.\nNothing you want to do here is semantically related to the later change\nto advise() you want to make. There are textual dependencies, which\nmeans you'll want to wait for one series to graduate before the other,\nbut that's already the case if you stuff the preparatory patch in this\nseries.\n\n-Peff\n"},{"id":"437895","messageId":"cover-v2-0.2-00000000000-20211004T142523Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-0.2-00000000000-20211004T013611Z-avarab@gmail.com","subject":"[PATCH v2 0/2] i18n: improve translatability of ambiguous object output","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T14:27:00Z","receivedAt":"2021-10-04T14:27:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A mostly-rewritten version in response to the discussion concluding at\nhttp://lore.kernel.org/git/YVrudGOcUxblsfPY@coredump.intra.peff.net;\nthanks a lot for the thorough review Jeff!\n\nÆvar Arnfjörð Bjarmason (2):\n  object.[ch]: mark object type names for translation\n  object-name: make ambiguous object output translatable\n\n object-name.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----\n object.c      | 27 ++++++++++++++++---\n object.h      |  1 +\n 3 files changed, 90 insertions(+), 10 deletions(-)\n\nRange-diff against v1:\n1:  7085f951a12 ! 1:  55bde16aa23 object-name tests: tighten up advise() output test\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    object-name tests: tighten up advise() output test\n    +    object.[ch]: mark object type names for translation\n     \n    -    Change tests added in 1ffa26c4614 (get_short_sha1: list ambiguous\n    -    objects on error, 2016-09-26) to only care about the OIDs that are\n    -    listed, which is what the test is trying to check for.\n    +    Mark the \"commit\", \"tree\", \"blob\" and \"tag\" types for translation, and\n    +    add an extern \"unknown type\" string for the OBJ_NONE case.\n     \n    -    This isn't needed by the subsequent commit, which won't change any of\n    -    the output, but a mere tightening of the tests assertions to more\n    -    closely match what we really want to test for here.\n    +    It is usually bad practice to translate individual words like this,\n    +    but for e.g. the list list output emitted by the \"short object ID dead\n    +    is ambiguous\" advice it makes sense.\n     \n    -    Now if the advise() message itself were change the phrasing around the\n    -    list of OIDs we won't have this test break. We're assuming that such\n    -    output won't have a need to indent anything except the OIDs.\n    +    A subsequent commit will make that output translatable, and use these\n    +    translation markings to do so. Well, we won't use \"commit\", but let's\n    +    mark it up anyway for consistency. It'll probably come in handy sooner\n    +    than later to have it already be translated, and it's to much of a\n    +    burden to place on translators if they're translating the other three\n    +    object types anyway.\n    +\n    +    Aside: I think it would probably make sense to change the \"NULL\" entry\n    +    for type_name() to be the \"unknown type\". I've ran into cases where\n    +    type_name() was unconditionally interpolated in e.g. an sprintf()\n    +    format, but let's leave that for #leftoverbits as that would be\n    +    changing the behavior of the type_name() function.\n    +\n    +    All of these will be new in the git.pot file, except \"blob\" which will\n    +    be shared with a \"cat-file\" command-line option, see\n    +    7bcf3414535 (cat-file --textconv/--filters: allow specifying the path\n    +    separately, 2016-09-09) for its introduction.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## t/t1512-rev-parse-disambiguation.sh ##\n    -@@ t/t1512-rev-parse-disambiguation.sh: test_expect_success 'ambiguity errors are not repeated (peel)' '\n    + ## object.c ##\n    +@@ object.c: struct object *get_indexed_object(unsigned int idx)\n      \n    - test_expect_success 'ambiguity hints' '\n    - \ttest_must_fail git rev-parse 000000000 2>stderr &&\n    --\tgrep ^hint: stderr >hints &&\n    --\t# 16 candidates, plus one intro line\n    --\ttest_line_count = 17 hints\n    -+\tgrep \"^hint:   \" stderr >hints &&\n    -+\t# 16 candidates, minus surrounding prose\n    -+\ttest_line_count = 16 hints\n    - '\n    + static const char *object_type_strings[] = {\n    + \tNULL,\t\t/* OBJ_NONE = 0 */\n    +-\t\"commit\",\t/* OBJ_COMMIT = 1 */\n    +-\t\"tree\",\t\t/* OBJ_TREE = 2 */\n    +-\t\"blob\",\t\t/* OBJ_BLOB = 3 */\n    +-\t\"tag\",\t\t/* OBJ_TAG = 4 */\n    ++\t/*\n    ++\t * TRANSLATORS: \"commit\", \"tree\", \"blob\" and \"tag\" are the\n    ++\t * name of Git's object types. These names are interpolated\n    ++\t * stand-alone when doing so is unambiguous for translation\n    ++\t * and doesn't require extra context. E.g. as part of an\n    ++\t * already-translated string that needs to have a type name\n    ++\t * quoted verbatim, or the short description of a command-line\n    ++\t * option expecting a given type.\n    ++\t */\n    ++\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n    ++\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n    ++\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n    ++\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n    + };\n      \n    - test_expect_success 'ambiguity hints respect type' '\n    - \ttest_must_fail git rev-parse 000000000^{commit} 2>stderr &&\n    --\tgrep ^hint: stderr >hints &&\n    --\t# 5 commits, 1 tag (which is a committish), plus intro line\n    --\ttest_line_count = 7 hints\n    -+\tgrep \"^hint:   \" stderr >hints &&\n    -+\t# 5 commits, 1 tag (which is a committish), minus surrounding prose\n    -+\ttest_line_count = 6 hints\n    - '\n    - \n    - test_expect_success 'failed type-selector still shows hint' '\n    -@@ t/t1512-rev-parse-disambiguation.sh: test_expect_success 'failed type-selector still shows hint' '\n    - \techo 851 | git hash-object --stdin -w &&\n    - \techo 872 | git hash-object --stdin -w &&\n    - \ttest_must_fail git rev-parse ee3d^{commit} 2>stderr &&\n    --\tgrep ^hint: stderr >hints &&\n    --\ttest_line_count = 3 hints\n    -+\tgrep \"^hint:   \" stderr >hints &&\n    -+\ttest_line_count = 2 hints\n    - '\n    ++/*\n    ++ * TRANSLATORS: This is the short type name of an object that's not\n    ++ * one of Git's known object types, as opposed to \"commit\", \"tree\",\n    ++ * \"blob\" and \"tag\" above.\n    ++ *\n    ++ * A user is unlikely to ever encounter these, but they can be\n    ++ * manually created with \"git hash-object --literally\".\n    ++ */\n    ++const char *unknown_type = N_(\"unknown type\");\n    ++\n    + const char *type_name(unsigned int type)\n    + {\n    + \tif (type >= ARRAY_SIZE(object_type_strings))\n    +\n    + ## object.h ##\n    +@@ object.h: struct object {\n    + \tstruct object_id oid;\n    + };\n      \n    - test_expect_success 'core.disambiguate config can prefer types' '\n    ++extern const char *unknown_type;\n    + const char *type_name(unsigned int type);\n    + int type_from_string_gently(const char *str, ssize_t, int gentle);\n    + #define type_from_string(str) type_from_string_gently(str, -1, 0)\n2:  b6136380c28 ! 2:  c0e873543f5 object-name: make ambiguous object output translatable\n    @@ Commit message\n         tweaked in [2] to be more friendly to translators. By being able to\n         customize the sprintf formats we're even ready for RTL languages.\n     \n    -    1. ef9b0370da6 (sha1-name.c: store and use repo in struct\n    -       disambiguate_state, 2019-04-16)\n    +    The \"unknown type\" message here is unreachable, and has been since\n    +    [1], i.e. that code has never worked. If we craft an object of a bogus\n    +    type with a conflicting prefix we'll just die:\n    +\n    +        $ git rev-parse 8315\n    +        error: short object ID 8315 is ambiguous\n    +        hint: The candidates are:\n    +        fatal: invalid object type\n    +\n    +    But let's continue to pretend that this works, we can eventually use\n    +    the API improvements in my ab/fsck-unexpected-type (once it lands) to\n    +    inspect these objects and emit the actual type here, or at least not\n    +    die as we emit \"unknown type\".\n    +\n    +    1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n    +       2016-09-26)\n         2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n            then SHA-1, 2018-05-10)\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## object-name.c ##\n    -@@ object-name.c: static int init_object_disambiguation(struct repository *r,\n    - \treturn 0;\n    - }\n    - \n    -+struct show_ambiguous_state {\n    -+\tconst struct disambiguate_state *ds;\n    -+\tstruct strbuf *advice;\n    -+};\n    -+\n    - static int show_ambiguous_object(const struct object_id *oid, void *data)\n    +@@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n      {\n    --\tconst struct disambiguate_state *ds = data;\n    -+\tstruct show_ambiguous_state *state = data;\n    -+\tconst struct disambiguate_state *ds = state->ds;\n    -+\tstruct strbuf *advice = state->advice;\n    + \tconst struct disambiguate_state *ds = data;\n      \tstruct strbuf desc = STRBUF_INIT;\n    ++\tstruct strbuf ci_ad = STRBUF_INIT;\n    ++\tstruct strbuf ci_s = STRBUF_INIT;\n      \tint type;\n    ++\tconst char *tag_desc = NULL;\n    ++\tconst char *abbrev;\n      \n    + \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n    + \t\treturn 0;\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n      \t\tif (commit) {\n      \t\t\tstruct pretty_print_context pp = {0};\n      \t\t\tpp.date_mode.type = DATE_SHORT;\n     -\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n    -+\t\t\tformat_commit_message(commit, _(\" %ad - %s\"), &desc, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%ad\", &ci_ad, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%s\", &ci_s, &pp);\n      \t\t}\n      \t} else if (type == OBJ_TAG) {\n      \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n      \t\tif (!parse_tag(tag) && tag->tag)\n     -\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    -+\t\t\tstrbuf_addf(&desc, _(\" %s\"), tag->tag);\n    ++\t\t\ttag_desc = tag->tag;\n      \t}\n      \n     -\tadvise(\"  %s %s%s\",\n     -\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n     -\t       type_name(type) ? type_name(type) : \"unknown type\",\n     -\t       desc.buf);\n    -+\tstrbuf_addf(advice,\n    -+\t\t    /*\n    -+\t\t     * TRANSLATORS: This is a line of ambiguous object\n    -+\t\t     * output. E.g.:\n    -+\t\t     *\n    -+\t\t     *    \"deadbeef commit 2021-01-01 - Some Commit Message\\n\"\n    -+\t\t     *    \"deadbeef tag Some Tag Message\\n\"\n    -+\t\t     *    \"deadbeef tree\\n\"\n    -+\t\t     *\n    -+\t\t     * I.e. the first argument is a short OID, the\n    -+\t\t     * second is the type name of the object, and the\n    -+\t\t     * third a description of the object, if it's a\n    -+\t\t     * commit or tag. In that case the \" %ad - %s\" and\n    -+\t\t     * \" %s\" formats above will be used for the third\n    -+\t\t     * argument.\n    -+\t\t     */\n    -+\t\t    _(\"  %s %s%s\\n\"),\n    -+\t\t    repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n    -+\t\t    type_name(type) ? type_name(type) : \"unknown type\",\n    -+\t\t    desc.buf);\n    - \n    - \tstrbuf_release(&desc);\n    - \treturn 0;\n    -@@ object-name.c: static enum get_oid_result get_short_oid(struct repository *r,\n    - \t}\n    - \n    - \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n    -+\t\tstruct strbuf sb = STRBUF_INIT;\n    - \t\tstruct oid_array collect = OID_ARRAY_INIT;\n    -+\t\tstruct show_ambiguous_state as = {\n    -+\t\t\t.ds = &ds,\n    -+\t\t\t.advice = &sb,\n    -+\t\t};\n    - \n    - \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n    - \n    -@@ object-name.c: static enum get_oid_result get_short_oid(struct repository *r,\n    - \t\tif (!ds.ambiguous)\n    - \t\t\tds.fn = NULL;\n    - \n    --\t\tadvise(_(\"The candidates are:\"));\n    - \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n    - \t\tsort_ambiguous_oid_array(r, &collect);\n    - \n    --\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n    -+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &as))\n    - \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n    -+\n    ++\tabbrev = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n    ++\tif (type == OBJ_COMMIT) {\n     +\t\t/*\n    -+\t\t * TRANSLATORS: The argument is the list of ambiguous\n    -+\t\t * objects composed in show_ambiguous_object(). See\n    -+\t\t * its \"TRANSLATORS\" comment for details.\n    ++\t\t * TRANSLATORS: This is a line of ambiguous commit\n    ++\t\t * object output. E.g.:\n    ++\t\t *\n    ++\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n    ++\t\t *\n    ++\t\t * The second argument is the \"commit\" string from\n    ++\t\t * object.c, it should (hopefully) already be\n    ++\t\t * translated.\n     +\t\t */\n    -+\t\tadvise(_(\"The candidates are:\\n\\n%s\"), sb.buf);\n    ++\t\tstrbuf_addf(&desc, _(\"%s %s %s - %s\"), abbrev, ci_ad.buf,\n    ++\t\t\t    _(type_name(type)), ci_s.buf);\n    ++\t} else if (tag_desc) {\n    ++\t\t/*\n    ++\t\t * TRANSLATORS: This is a line of\n    ++\t\t * ambiguous tag object output. E.g.:\n    ++\t\t *\n    ++\t\t *    \"deadbeef tag Some Tag Message\"\n    ++\t\t *\n    ++\t\t * The second argument is the \"tag\" string from\n    ++\t\t * object.c, it should (hopefully) already be\n    ++\t\t * translated.\n    ++\t\t */\n    ++\t\tstrbuf_addf(&desc, _(\"%s %s %s\"), abbrev, _(type_name(type)),\n    ++\t\t\t    tag_desc);\n    ++\t} else {\n    ++\t\tconst char *tname = type_name(type) ? _(type_name(type)) :\n    ++\t\t\t_(unknown_type);\n    ++\t\t/*\n    ++\t\t * TRANSLATORS: This is a line of ambiguous <type>\n    ++\t\t * object output. Where <type> is one of the object\n    ++\t\t * types of \"tree\", \"blob\", \"tag\" (\"commit\" is handled\n    ++\t\t * above).\n    ++\t\t *\n    ++\t\t *    \"deadbeef tree\"\n    ++\t\t *    \"deadbeef blob\"\n    ++\t\t *    \"deadbeef tag\"\n    ++\t\t *    \"deadbeef unknown type\"\n    ++\t\t *\n    ++\t\t * Note that annotated tags use a separate format\n    ++\t\t * outlined above.\n    ++\t\t *\n    ++\t\t * The second argument is the \"tree\", \"blob\" or \"tag\"\n    ++\t\t * string from object.c, or the \"unknown type\" string\n    ++\t\t * in the case of an unknown type. All of them should\n    ++\t\t * (hopefully) already be translated.\n    ++\t\t */\n    ++\t\tstrbuf_addf(&desc, _(\"%s %s\"), abbrev, tname);\n    ++\t}\n     +\n    - \t\toid_array_clear(&collect);\n    - \t}\n    ++\t/*\n    ++\t * TRANSLATORS: This is line item of ambiguous object output,\n    ++\t * translated above.\n    ++\t */\n    ++\tadvise(_(\"  %s\\n\"), desc.buf);\n    + \n    + \tstrbuf_release(&desc);\n    ++\tstrbuf_release(&ci_ad);\n    ++\tstrbuf_release(&ci_s);\n    + \treturn 0;\n    + }\n      \n-- \n2.33.0.1409.ge73c1ecc5b4\n\n"},{"id":"437896","messageId":"patch-v2-1.2-55bde16aa23-20211004T142523Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v2-0.2-00000000000-20211004T142523Z-avarab@gmail.com","subject":"[PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T14:27:01Z","receivedAt":"2021-10-04T14:27:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Mark the \"commit\", \"tree\", \"blob\" and \"tag\" types for translation, and\nadd an extern \"unknown type\" string for the OBJ_NONE case.\n\nIt is usually bad practice to translate individual words like this,\nbut for e.g. the list list output emitted by the \"short object ID dead\nis ambiguous\" advice it makes sense.\n\nA subsequent commit will make that output translatable, and use these\ntranslation markings to do so. Well, we won't use \"commit\", but let's\nmark it up anyway for consistency. It'll probably come in handy sooner\nthan later to have it already be translated, and it's to much of a\nburden to place on translators if they're translating the other three\nobject types anyway.\n\nAside: I think it would probably make sense to change the \"NULL\" entry\nfor type_name() to be the \"unknown type\". I've ran into cases where\ntype_name() was unconditionally interpolated in e.g. an sprintf()\nformat, but let's leave that for #leftoverbits as that would be\nchanging the behavior of the type_name() function.\n\nAll of these will be new in the git.pot file, except \"blob\" which will\nbe shared with a \"cat-file\" command-line option, see\n7bcf3414535 (cat-file --textconv/--filters: allow specifying the path\nseparately, 2016-09-09) for its introduction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object.c | 27 +++++++++++++++++++++++----\n object.h |  1 +\n 2 files changed, 24 insertions(+), 4 deletions(-)\n\ndiff --git a/object.c b/object.c\nindex 4e85955a941..47dbe0d8a2a 100644\n--- a/object.c\n+++ b/object.c\n@@ -22,12 +22,31 @@ struct object *get_indexed_object(unsigned int idx)\n \n static const char *object_type_strings[] = {\n \tNULL,\t\t/* OBJ_NONE = 0 */\n-\t\"commit\",\t/* OBJ_COMMIT = 1 */\n-\t\"tree\",\t\t/* OBJ_TREE = 2 */\n-\t\"blob\",\t\t/* OBJ_BLOB = 3 */\n-\t\"tag\",\t\t/* OBJ_TAG = 4 */\n+\t/*\n+\t * TRANSLATORS: \"commit\", \"tree\", \"blob\" and \"tag\" are the\n+\t * name of Git's object types. These names are interpolated\n+\t * stand-alone when doing so is unambiguous for translation\n+\t * and doesn't require extra context. E.g. as part of an\n+\t * already-translated string that needs to have a type name\n+\t * quoted verbatim, or the short description of a command-line\n+\t * option expecting a given type.\n+\t */\n+\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n+\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n+\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n+\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n };\n \n+/*\n+ * TRANSLATORS: This is the short type name of an object that's not\n+ * one of Git's known object types, as opposed to \"commit\", \"tree\",\n+ * \"blob\" and \"tag\" above.\n+ *\n+ * A user is unlikely to ever encounter these, but they can be\n+ * manually created with \"git hash-object --literally\".\n+ */\n+const char *unknown_type = N_(\"unknown type\");\n+\n const char *type_name(unsigned int type)\n {\n \tif (type >= ARRAY_SIZE(object_type_strings))\ndiff --git a/object.h b/object.h\nindex 549f2d256bc..0510dc4b3ea 100644\n--- a/object.h\n+++ b/object.h\n@@ -91,6 +91,7 @@ struct object {\n \tstruct object_id oid;\n };\n \n+extern const char *unknown_type;\n const char *type_name(unsigned int type);\n int type_from_string_gently(const char *str, ssize_t, int gentle);\n #define type_from_string(str) type_from_string_gently(str, -1, 0)\n-- \n2.33.0.1409.ge73c1ecc5b4\n\n"},{"id":"437897","messageId":"patch-v2-2.2-c0e873543f5-20211004T142523Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v2-0.2-00000000000-20211004T142523Z-avarab@gmail.com","subject":"[PATCH v2 2/2] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-04T14:27:02Z","receivedAt":"2021-10-04T14:27:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] to be more friendly to translators. By being able to\ncustomize the sprintf formats we're even ready for RTL languages.\n\nThe \"unknown type\" message here is unreachable, and has been since\n[1], i.e. that code has never worked. If we craft an object of a bogus\ntype with a conflicting prefix we'll just die:\n\n    $ git rev-parse 8315\n    error: short object ID 8315 is ambiguous\n    hint: The candidates are:\n    fatal: invalid object type\n\nBut let's continue to pretend that this works, we can eventually use\nthe API improvements in my ab/fsck-unexpected-type (once it lands) to\ninspect these objects and emit the actual type here, or at least not\ndie as we emit \"unknown type\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 66 insertions(+), 6 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..73c946f1117 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -355,7 +355,11 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n+\tstruct strbuf ci_ad = STRBUF_INIT;\n+\tstruct strbuf ci_s = STRBUF_INIT;\n \tint type;\n+\tconst char *tag_desc = NULL;\n+\tconst char *abbrev;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n@@ -366,20 +370,76 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &ci_ad, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &ci_s, &pp);\n \t\t}\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_desc = tag->tag;\n \t}\n \n-\tadvise(\"  %s %s%s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n-\t       desc.buf);\n+\tabbrev = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n+\tif (type == OBJ_COMMIT) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t *\n+\t\t * The second argument is the \"commit\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s %s %s - %s\"), abbrev, ci_ad.buf,\n+\t\t\t    _(type_name(type)), ci_s.buf);\n+\t} else if (tag_desc) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s %s %s\"), abbrev, _(type_name(type)),\n+\t\t\t    tag_desc);\n+\t} else {\n+\t\tconst char *tname = type_name(type) ? _(type_name(type)) :\n+\t\t\t_(unknown_type);\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. Where <type> is one of the object\n+\t\t * types of \"tree\", \"blob\", \"tag\" (\"commit\" is handled\n+\t\t * above).\n+\t\t *\n+\t\t *    \"deadbeef tree\"\n+\t\t *    \"deadbeef blob\"\n+\t\t *    \"deadbeef tag\"\n+\t\t *    \"deadbeef unknown type\"\n+\t\t *\n+\t\t * Note that annotated tags use a separate format\n+\t\t * outlined above.\n+\t\t *\n+\t\t * The second argument is the \"tree\", \"blob\" or \"tag\"\n+\t\t * string from object.c, or the \"unknown type\" string\n+\t\t * in the case of an unknown type. All of them should\n+\t\t * (hopefully) already be translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s %s\"), abbrev, tname);\n+\t}\n+\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output,\n+\t * translated above.\n+\t */\n+\tadvise(_(\"  %s\\n\"), desc.buf);\n \n \tstrbuf_release(&desc);\n+\tstrbuf_release(&ci_ad);\n+\tstrbuf_release(&ci_s);\n \treturn 0;\n }\n \n-- \n2.33.0.1409.ge73c1ecc5b4\n\n"},{"id":"437932","messageId":"CAPig+cQYU+kgJrtP_Y5ZH8NpZywChxkaYjnm8OFrj8VbFWLR1w@mail.gmail.com","threadId":"56641","inReplyTo":"patch-v2-1.2-55bde16aa23-20211004T142523Z-avarab@gmail.com","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-10-04T18:54:11Z","receivedAt":"2021-10-04T18:54:25Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Oct 4, 2021 at 10:27 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Mark the \"commit\", \"tree\", \"blob\" and \"tag\" types for translation, and\n> add an extern \"unknown type\" string for the OBJ_NONE case.\n>\n> It is usually bad practice to translate individual words like this,\n> but for e.g. the list list output emitted by the \"short object ID dead\n\n\"list list\"?\n\n> is ambiguous\" advice it makes sense.\n>\n> A subsequent commit will make that output translatable, and use these\n> translation markings to do so. Well, we won't use \"commit\", but let's\n> mark it up anyway for consistency. It'll probably come in handy sooner\n> than later to have it already be translated, and it's to much of a\n> burden to place on translators if they're translating the other three\n> object types anyway.\n\nAt first I thought you meant s/to much/too much/, but that doesn't\nseem to make sense (unless I'm misunderstanding), so perhaps you mean\ns/to/not/.\n\n> Aside: I think it would probably make sense to change the \"NULL\" entry\n> for type_name() to be the \"unknown type\". I've ran into cases where\n> type_name() was unconditionally interpolated in e.g. an sprintf()\n> format, but let's leave that for #leftoverbits as that would be\n> changing the behavior of the type_name() function.\n>\n> All of these will be new in the git.pot file, except \"blob\" which will\n> be shared with a \"cat-file\" command-line option, see\n> 7bcf3414535 (cat-file --textconv/--filters: allow specifying the path\n> separately, 2016-09-09) for its introduction.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"437964","messageId":"5d47caad-7f0b-f6ce-b055-dc21d58892cc@gmail.com","threadId":"56641","inReplyTo":"patch-v2-1.2-55bde16aa23-20211004T142523Z-avarab@gmail.com","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-10-05T09:37:12Z","receivedAt":"2021-10-05T09:37:18Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 04/10/21 21.27, Ævar Arnfjörð Bjarmason wrote:\n>   static const char *object_type_strings[] = {\n>   \tNULL,\t\t/* OBJ_NONE = 0 */\n> -\t\"commit\",\t/* OBJ_COMMIT = 1 */\n> -\t\"tree\",\t\t/* OBJ_TREE = 2 */\n> -\t\"blob\",\t\t/* OBJ_BLOB = 3 */\n> -\t\"tag\",\t\t/* OBJ_TAG = 4 */\n> +\t/*\n> +\t * TRANSLATORS: \"commit\", \"tree\", \"blob\" and \"tag\" are the\n> +\t * name of Git's object types. These names are interpolated\n> +\t * stand-alone when doing so is unambiguous for translation\n> +\t * and doesn't require extra context. E.g. as part of an\n> +\t * already-translated string that needs to have a type name\n> +\t * quoted verbatim, or the short description of a command-line\n> +\t * option expecting a given type.\n> +\t */\n> +\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n> +\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n> +\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n> +\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n>   };\n>   \n\nAre these object type names safe for translating? (e.g. can they be \ntranslatable without affecting private API string, which aren't \ntranslatable)?\n\n> +/*\n> + * TRANSLATORS: This is the short type name of an object that's not\n> + * one of Git's known object types, as opposed to \"commit\", \"tree\",\n> + * \"blob\" and \"tag\" above.\n> + *\n> + * A user is unlikely to ever encounter these, but they can be\n> + * manually created with \"git hash-object --literally\".\n> + */\n> +const char *unknown_type = N_(\"unknown type\");\n> +\n>   const char *type_name(unsigned int type)\n\nDid you mean that \"unknown type\" is generic shorthand?\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"437979","messageId":"874k9vlawx.fsf@evledraar.gmail.com","threadId":"56641","inReplyTo":"5d47caad-7f0b-f6ce-b055-dc21d58892cc@gmail.com","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-05T15:52:36Z","receivedAt":"2021-10-05T16:00:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Oct 05 2021, Bagas Sanjaya wrote:\n\n> On 04/10/21 21.27, Ævar Arnfjörð Bjarmason wrote:\n>>   static const char *object_type_strings[] = {\n>>   \tNULL,\t\t/* OBJ_NONE = 0 */\n>> -\t\"commit\",\t/* OBJ_COMMIT = 1 */\n>> -\t\"tree\",\t\t/* OBJ_TREE = 2 */\n>> -\t\"blob\",\t\t/* OBJ_BLOB = 3 */\n>> -\t\"tag\",\t\t/* OBJ_TAG = 4 */\n>> +\t/*\n>> +\t * TRANSLATORS: \"commit\", \"tree\", \"blob\" and \"tag\" are the\n>> +\t * name of Git's object types. These names are interpolated\n>> +\t * stand-alone when doing so is unambiguous for translation\n>> +\t * and doesn't require extra context. E.g. as part of an\n>> +\t * already-translated string that needs to have a type name\n>> +\t * quoted verbatim, or the short description of a command-line\n>> +\t * option expecting a given type.\n>> +\t */\n>> +\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n>> +\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n>> +\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n>> +\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n>>   };\n>>   \n>\n> Are these object type names safe for translating? (e.g. can they be\n> translatable without affecting private API string, which aren't \n> translatable)?\n\nYes, the N_() macro is always a noop. It's just there so the i18n\ntooling knows to pick up these strings and drop them into\npo/git.pot. See po/README.md for details.\n\nIt does change the behavior of any code that later does\n_(type_name(type)), as the string will then (potentially) be found in\nthe *.mo files, but as shown in 2/2 that needs to be added to each\ncallsite manually. So we're not going to translate \"ls-tree\" output or\nwhatever just because it has \"tree\" etc. in it.\n\n>> +/*\n>> + * TRANSLATORS: This is the short type name of an object that's not\n>> + * one of Git's known object types, as opposed to \"commit\", \"tree\",\n>> + * \"blob\" and \"tag\" above.\n>> + *\n>> + * A user is unlikely to ever encounter these, but they can be\n>> + * manually created with \"git hash-object --literally\".\n>> + */\n>> +const char *unknown_type = N_(\"unknown type\");\n>> +\n>>   const char *type_name(unsigned int type)\n>\n> Did you mean that \"unknown type\" is generic shorthand?\n\nYes, we could get the actual type name here, but it's a bit of a pain,\nand as noted in 2/2 this code doesn't work anyway (which pre-dates this\nseries).\n\nBut I'll see if I'll remember to loop around to fixing it after my\nfsck/object library fixes related to this land, but for now just marking\nthis for translation makes senes I think.\n"},{"id":"438085","messageId":"YV3zZFOJd6blVGXn@coredump.intra.peff.net","threadId":"56641","inReplyTo":"patch-v2-1.2-55bde16aa23-20211004T142523Z-avarab@gmail.com","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-06T19:05:08Z","receivedAt":"2021-10-06T19:05:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 04:27:01PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Mark the \"commit\", \"tree\", \"blob\" and \"tag\" types for translation, and\n> add an extern \"unknown type\" string for the OBJ_NONE case.\n> \n> It is usually bad practice to translate individual words like this,\n> but for e.g. the list list output emitted by the \"short object ID dead\n> is ambiguous\" advice it makes sense.\n\nWe already seem to have a translatable string for \"commit\", but if I\nlook at say es.po, the translation is \"confirmar\", which is considering\nit a verb. Now my Spanish is pretty rusty, so it's possible this works\nas a noun, too. But if I look at other messages, like:\n\n  #: builtin/commit.c:1623\n  msgid \"override date for commit\"\n  msgstr \"sobrescribe la fecha del commit\"\n\n  #: builtin/commit.c:1626\n  msgid \"reuse message from specified commit\"\n  msgstr \"reusar el mensaje de un commit específico\"\n\nthen it's clear that \"commit\" as a noun is translated as \"commit\". I'm\nnot sure what facilities (if any) there are in gettext for having the\nsame string in different contexts.\n\nI do note that this is already a problem. Of the five spots listed:\n\n  #: builtin/commit.c:1625 builtin/commit.c:1626 builtin/commit.c:1632\n  #: parse-options.h:329 ref-filter.h:90\n  msgid \"commit\"\n  msgstr \"confirmar\"\n\nThey all appear to want is as a noun. So maybe this is just\nmis-translated for Spanish. It does feel like an accident in the making,\nthough.\n\n> A subsequent commit will make that output translatable, and use these\n> translation markings to do so. Well, we won't use \"commit\", but let's\n> mark it up anyway for consistency. It'll probably come in handy sooner\n> than later to have it already be translated, and it's to much of a\n> burden to place on translators if they're translating the other three\n> object types anyway.\n\nI do wonder how useful it is to translate these type names in general.\nEspecially as used in this series, they're really technical terms, and\nyou are not going to escape the name \"git commit\" as a command. But I\ndon't ever use translated Git, so I'm not sure my opinion is all that\nmeaningful there.\n\n> Aside: I think it would probably make sense to change the \"NULL\" entry\n> for type_name() to be the \"unknown type\". I've ran into cases where\n> type_name() was unconditionally interpolated in e.g. an sprintf()\n> format, but let's leave that for #leftoverbits as that would be\n> changing the behavior of the type_name() function.\n\nIMHO this would be a bad idea. Even if there is a spot that uses the\nresult without checking for NULL, I'd much rather have Git segfault than\nsay, write out an object with a bogus name (as it would in index_mem(),\nfor example). So you really have to look over every caller, at which\npoint you may as well adjust the ones that aren't checking for NULL.\n\nNow if you introduced type_name_human(), which auto-translated and\nconverted NULL to \"unknown\", then that would be easy to plug in\nappropriately as you audited the callers.\n\n>  static const char *object_type_strings[] = {\n>  \tNULL,\t\t/* OBJ_NONE = 0 */\n> -\t\"commit\",\t/* OBJ_COMMIT = 1 */\n> -\t\"tree\",\t\t/* OBJ_TREE = 2 */\n> -\t\"blob\",\t\t/* OBJ_BLOB = 3 */\n> -\t\"tag\",\t\t/* OBJ_TAG = 4 */\n> +\t/*\n> +\t * TRANSLATORS: \"commit\", \"tree\", \"blob\" and \"tag\" are the\n> +\t * name of Git's object types. These names are interpolated\n> +\t * stand-alone when doing so is unambiguous for translation\n> +\t * and doesn't require extra context. E.g. as part of an\n> +\t * already-translated string that needs to have a type name\n> +\t * quoted verbatim, or the short description of a command-line\n> +\t * option expecting a given type.\n> +\t */\n> +\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n> +\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n> +\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n> +\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n>  };\n\nThis does make me feel slightly uneasy, just because so many parts of\nGit rely on these _not_ being translated. But I see in your other\nresponse that N_() really does nothing. So aside from possibly\nmisleading readers of the code, I think this is probably OK.\n\n-Peff\n"},{"id":"438086","messageId":"YV302GvgjqYhid5v@coredump.intra.peff.net","threadId":"56641","inReplyTo":"patch-v2-2.2-c0e873543f5-20211004T142523Z-avarab@gmail.com","subject":"Re: [PATCH v2 2/2] object-name: make ambiguous object output translatable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-06T19:11:20Z","receivedAt":"2021-10-06T19:11:24Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 04, 2021 at 04:27:02PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> +\tabbrev = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n> +\tif (type == OBJ_COMMIT) {\n> +\t\t/*\n> +\t\t * TRANSLATORS: This is a line of ambiguous commit\n> +\t\t * object output. E.g.:\n> +\t\t *\n> +\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n> +\t\t *\n> +\t\t * The second argument is the \"commit\" string from\n> +\t\t * object.c, it should (hopefully) already be\n> +\t\t * translated.\n> +\t\t */\n> +\t\tstrbuf_addf(&desc, _(\"%s %s %s - %s\"), abbrev, ci_ad.buf,\n> +\t\t\t    _(type_name(type)), ci_s.buf);\n> +\t} else if (tag_desc) {\n> [...]\n\nOK, this all looks reasonable to me. I'd probably have ditched \"desc\"\naltogether in favor of just calling advise(), to give translators even\nmore information about what we're trying to output, but I admit I don't\ncare that much either way.\n\nI'm still not sure if translating the object types is a good idea or\nnot, per my other response.\n\n> +\t/*\n> +\t * TRANSLATORS: This is line item of ambiguous object output,\n> +\t * translated above.\n> +\t */\n> +\tadvise(_(\"  %s\\n\"), desc.buf);\n\nThe \"\\n\" here isn't necessary (and wasn't present in the original, but\nit doesn't hurt, as advise()'s algorithm gobbles any newlines as it\nsplits). I guess it helps making this otherwise un-notable string more\nunique for translation, but just stuffing the indentation into the\nearlier calls would do an even better job of that.\n\n-Peff\n"},{"id":"438091","messageId":"xmqqv92aq6m3.fsf@gitster.g","threadId":"56641","inReplyTo":"YV3zZFOJd6blVGXn@coredump.intra.peff.net","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-06T19:46:12Z","receivedAt":"2021-10-06T19:46:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> They all appear to want is as a noun. So maybe this is just\n> mis-translated for Spanish. It does feel like an accident in the making,\n> though.\n\nProbably we need pgettext().\n\nhttps://www.gnu.org/software/gettext/manual/html_node/Contexts.html\n\n> I do wonder how useful it is to translate these type names in general.\n> Especially as used in this series, they're really technical terms, and\n> you are not going to escape the name \"git commit\" as a command.\n\nI share the same feeling (I do not use translated git, either).\n\n> Now if you introduced type_name_human(), which auto-translated and\n> converted NULL to \"unknown\", then that would be easy to plug in\n> appropriately as you audited the callers.\n\nYes.\n\n>\n>>  static const char *object_type_strings[] = {\n>> ...\n>> +\tN_(\"commit\"),\t/* OBJ_COMMIT = 1 */\n>> +\tN_(\"tree\"),\t/* OBJ_TREE = 2 */\n>> +\tN_(\"blob\"),\t/* OBJ_BLOB = 3 */\n>> +\tN_(\"tag\"),\t/* OBJ_TAG = 4 */\n>>  };\n>\n> This does make me feel slightly uneasy, just because so many parts of\n> Git rely on these _not_ being translated. But I see in your other\n> response that N_() really does nothing. So aside from possibly\n> misleading readers of the code, I think this is probably OK.\n\nYes, this may be scary looking but the least risky part of this\npatch, as N_() is no-op at runtime ;-).\n\n"},{"id":"438098","messageId":"YV4JQytrc9UTa22o@coredump.intra.peff.net","threadId":"56641","inReplyTo":"xmqqv92aq6m3.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-10-06T20:38:27Z","receivedAt":"2021-10-06T20:38:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 06, 2021 at 12:46:12PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > They all appear to want is as a noun. So maybe this is just\n> > mis-translated for Spanish. It does feel like an accident in the making,\n> > though.\n> \n> Probably we need pgettext().\n> \n> https://www.gnu.org/software/gettext/manual/html_node/Contexts.html\n\nYeah, that make sense. I'm not sure how it interacts with N_(), though.\nI.e., I'd expect the \"context\" to ride along with the original string,\nbut I guess it is really in the caller who's translating it. So the real\nspot becomes:\n\n  printf(_(\"my type is %s\"), pgettext(\"object-type\", type_name(type)));\n\nIt's a little unfortunate that every caller has to do it rather than\nputting it near the source string. But I guess a type_name_human() would\nsolve that, too. ;)\n\n-Peff\n"},{"id":"438186","messageId":"xmqq35pcr9oc.fsf@gitster.g","threadId":"56641","inReplyTo":"YV4JQytrc9UTa22o@coredump.intra.peff.net","subject":"Re: [PATCH v2 1/2] object.[ch]: mark object type names for translation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-07T18:06:59Z","receivedAt":"2021-10-07T18:07:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Oct 06, 2021 at 12:46:12PM -0700, Junio C Hamano wrote:\n>\n>> Jeff King <peff@peff.net> writes:\n>> \n>> > They all appear to want is as a noun. So maybe this is just\n>> > mis-translated for Spanish. It does feel like an accident in the making,\n>> > though.\n>> \n>> Probably we need pgettext().\n>> \n>> https://www.gnu.org/software/gettext/manual/html_node/Contexts.html\n>\n> Yeah, that make sense. I'm not sure how it interacts with N_(), though.\n> I.e., I'd expect the \"context\" to ride along with the original string,\n> but I guess it is really in the caller who's translating it. So the real\n> spot becomes:\n>\n>   printf(_(\"my type is %s\"), pgettext(\"object-type\", type_name(type)));\n>\n> It's a little unfortunate that every caller has to do it rather than\n> putting it near the source string. But I guess a type_name_human() would\n> solve that, too. ;)\n\nYes, I agree the need for pgettext() is annoying but I do not see an\neasy alternative.  Introducing a wrapper like type_name_human() to\nlimit the damage sounds like the best we could do.\n\nThanks.\n"},{"id":"438327","messageId":"cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v2-0.2-00000000000-20211004T142523Z-avarab@gmail.com","subject":"[PATCH v3 0/3] i18n: improve translatability of ambiguous object output","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-08T19:34:45Z","receivedAt":"2021-10-08T19:34:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since v2 the \"commit\", \"tag\" etc. types in object.c are no longer\nmarked for translation.\n\nThere's a new 1/3 where we lead with an assert() and commit message\nshowing that the existing \"unknown type\" code is gone, which makes\nwhat comes after simpler.\n\nIn 2/3 we no longer have to deal with special-cases related to corrupt\nor otherwise bad objects, which makes for less work for translators.\n\nIn 3/3 I added the tag date to ambiguous tag objects, which is now\nconsistent with how commit objects are shown.\n\nÆvar Arnfjörð Bjarmason (3):\n  object-name: remove unreachable \"unknown type\" handling\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n\n object-name.c | 68 +++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 61 insertions(+), 7 deletions(-)\n\nRange-diff against v2:\n1:  55bde16aa23 < -:  ----------- object.[ch]: mark object type names for translation\n-:  ----------- > 1:  fb29e10ee35 object-name: remove unreachable \"unknown type\" handling\n2:  c0e873543f5 ! 2:  587a5717e47 object-name: make ambiguous object output translatable\n    @@ Commit message\n         object-name: make ambiguous object output translatable\n     \n         Change the output of show_ambiguous_object() added in [1] and last\n    -    tweaked in [2] to be more friendly to translators. By being able to\n    -    customize the sprintf formats we're even ready for RTL languages.\n    -\n    -    The \"unknown type\" message here is unreachable, and has been since\n    -    [1], i.e. that code has never worked. If we craft an object of a bogus\n    -    type with a conflicting prefix we'll just die:\n    -\n    -        $ git rev-parse 8315\n    -        error: short object ID 8315 is ambiguous\n    -        hint: The candidates are:\n    -        fatal: invalid object type\n    -\n    -    But let's continue to pretend that this works, we can eventually use\n    -    the API improvements in my ab/fsck-unexpected-type (once it lands) to\n    -    inspect these objects and emit the actual type here, or at least not\n    -    die as we emit \"unknown type\".\n    +    tweaked in [2] and the preceding commit to be more friendly to\n    +    translators. By being able to customize the \"<SP><SP>%s\\n\" format\n    +    we're even ready for RTL languages, who'd presumably like to change\n    +    that to \"%s<SP><SP>\\n\".\n     \n         1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n            2016-09-26)\n    @@ Commit message\n     \n      ## object-name.c ##\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - {\n      \tconst struct disambiguate_state *ds = data;\n      \tstruct strbuf desc = STRBUF_INIT;\n    -+\tstruct strbuf ci_ad = STRBUF_INIT;\n    -+\tstruct strbuf ci_s = STRBUF_INIT;\n      \tint type;\n    -+\tconst char *tag_desc = NULL;\n    -+\tconst char *abbrev;\n    ++\tconst char *hash;\n      \n      \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n      \t\treturn 0;\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    + \ttype = oid_object_info(ds->repo, oid, NULL);\n    + \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n    + \t       type == OBJ_BLOB || type == OBJ_TAG);\n    ++\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n    ++\n    + \tif (type == OBJ_COMMIT) {\n    ++\t\tstruct strbuf ad = STRBUF_INIT;\n    ++\t\tstruct strbuf s = STRBUF_INIT;\n    + \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n    ++\n      \t\tif (commit) {\n      \t\t\tstruct pretty_print_context pp = {0};\n      \t\t\tpp.date_mode.type = DATE_SHORT;\n     -\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n    -+\t\t\tformat_commit_message(commit, \"%ad\", &ci_ad, &pp);\n    -+\t\t\tformat_commit_message(commit, \"%s\", &ci_s, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n      \t\t}\n    - \t} else if (type == OBJ_TAG) {\n    - \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n    - \t\tif (!parse_tag(tag) && tag->tag)\n    --\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    -+\t\t\ttag_desc = tag->tag;\n    - \t}\n    - \n    --\tadvise(\"  %s %s%s\",\n    --\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n    --\t       type_name(type) ? type_name(type) : \"unknown type\",\n    --\t       desc.buf);\n    -+\tabbrev = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n    -+\tif (type == OBJ_COMMIT) {\n    ++\n     +\t\t/*\n     +\t\t * TRANSLATORS: This is a line of ambiguous commit\n     +\t\t * object output. E.g.:\n     +\t\t *\n     +\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n    -+\t\t *\n    -+\t\t * The second argument is the \"commit\" string from\n    -+\t\t * object.c, it should (hopefully) already be\n    -+\t\t * translated.\n     +\t\t */\n    -+\t\tstrbuf_addf(&desc, _(\"%s %s %s - %s\"), abbrev, ci_ad.buf,\n    -+\t\t\t    _(type_name(type)), ci_s.buf);\n    -+\t} else if (tag_desc) {\n    ++\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n    ++\n    ++\t\tstrbuf_release(&ad);\n    ++\t\tstrbuf_release(&s);\n    + \t} else if (type == OBJ_TAG) {\n    + \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n    ++\t\tconst char *tag_tag = \"\";\n    ++\n    + \t\tif (!parse_tag(tag) && tag->tag)\n    +-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    ++\t\t\ttag_tag = tag->tag;\n    ++\n     +\t\t/*\n     +\t\t * TRANSLATORS: This is a line of\n     +\t\t * ambiguous tag object output. E.g.:\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\t * object.c, it should (hopefully) already be\n     +\t\t * translated.\n     +\t\t */\n    -+\t\tstrbuf_addf(&desc, _(\"%s %s %s\"), abbrev, _(type_name(type)),\n    -+\t\t\t    tag_desc);\n    -+\t} else {\n    -+\t\tconst char *tname = type_name(type) ? _(type_name(type)) :\n    -+\t\t\t_(unknown_type);\n    ++\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n    ++\t} else if (type == OBJ_TREE) {\n     +\t\t/*\n     +\t\t * TRANSLATORS: This is a line of ambiguous <type>\n    -+\t\t * object output. Where <type> is one of the object\n    -+\t\t * types of \"tree\", \"blob\", \"tag\" (\"commit\" is handled\n    -+\t\t * above).\n    -+\t\t *\n    -+\t\t *    \"deadbeef tree\"\n    -+\t\t *    \"deadbeef blob\"\n    -+\t\t *    \"deadbeef tag\"\n    -+\t\t *    \"deadbeef unknown type\"\n    -+\t\t *\n    -+\t\t * Note that annotated tags use a separate format\n    -+\t\t * outlined above.\n    -+\t\t *\n    -+\t\t * The second argument is the \"tree\", \"blob\" or \"tag\"\n    -+\t\t * string from object.c, or the \"unknown type\" string\n    -+\t\t * in the case of an unknown type. All of them should\n    -+\t\t * (hopefully) already be translated.\n    ++\t\t * object output. E.g. \"deadbeef tree\".\n     +\t\t */\n    -+\t\tstrbuf_addf(&desc, _(\"%s %s\"), abbrev, tname);\n    -+\t}\n    -+\n    ++\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n    ++\t} else if (type == OBJ_BLOB) {\n    ++\t\t/*\n    ++\t\t * TRANSLATORS: This is a line of ambiguous <type>\n    ++\t\t * object output. E.g. \"deadbeef blob\".\n    ++\t\t */\n    ++\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n    ++\t} else {\n    ++\t\tBUG(\"unreachable\");\n    + \t}\n    + \n    +-\tadvise(\"  %s %s%s\",\n    +-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n    +-\t       type_name(type), desc.buf);\n     +\t/*\n     +\t * TRANSLATORS: This is line item of ambiguous object output,\n     +\t * translated above.\n     +\t */\n    -+\tadvise(_(\"  %s\\n\"), desc.buf);\n    ++\tadvise(_(\"  %s\"), desc.buf);\n      \n      \tstrbuf_release(&desc);\n    -+\tstrbuf_release(&ci_ad);\n    -+\tstrbuf_release(&ci_s);\n      \treturn 0;\n    - }\n    - \n-:  ----------- > 3:  8bde4e174b7 object-name: show date for ambiguous tag objects\n-- \n2.33.0.1492.g76eb1af92bc\n\n"},{"id":"438328","messageId":"patch-v3-1.3-fb29e10ee35-20211008T193041Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com","subject":"[PATCH v3 1/3] object-name: remove unreachable \"unknown type\" handling","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-08T19:34:46Z","receivedAt":"2021-10-08T19:34:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"unknown type\" handling when displaying the ambiguous\nobject list. See [1] for the current output, and [1] for the commit\nthat added the \"unknown type\" handling.\n\nThe reason this code wasn't reachable is because we're not passing in\nOBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll just die in sort_ambiguous()\nbefore we get to show_ambiguous_object():\n\n    $ git rev-parse 8315\n    error: short object ID 8315 is ambiguous\n    hint: The candidates are:\n    fatal: invalid object type\n\nWe should do better here, but let's leave that for some future\nimprovement. In a subsequent commit I'll improve the output we do\nshow, and not having to handle the \"unknown type\" case simplifies that\nchange.\n\nEven though we know that this isn't reachable let's back that up with\nan assert() both for self-documentation and sanity checking.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..59e934262e7 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -376,8 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \n \tadvise(\"  %s %s%s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n-\t       desc.buf);\n+\t       type_name(type), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.33.0.1492.g76eb1af92bc\n\n"},{"id":"438329","messageId":"patch-v3-2.3-587a5717e47-20211008T193041Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com","subject":"[PATCH v3 2/3] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-08T19:34:47Z","receivedAt":"2021-10-08T19:35:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators. By being able to customize the \"<SP><SP>%s\\n\" format\nwe're even ready for RTL languages, who'd presumably like to change\nthat to \"%s<SP><SP>\\n\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 53 insertions(+), 5 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 59e934262e7..7a5355b4cf7 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,6 +356,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n@@ -363,22 +364,69 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n+\n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf ad = STRBUF_INIT;\n+\t\tstruct strbuf s = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n+\n+\t\tstrbuf_release(&ad);\n+\t\tstrbuf_release(&s);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n+\t\tconst char *tag_tag = \"\";\n+\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_tag = tag->tag;\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t} else {\n+\t\tBUG(\"unreachable\");\n \t}\n \n-\tadvise(\"  %s %s%s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type), desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output,\n+\t * translated above.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.33.0.1492.g76eb1af92bc\n\n"},{"id":"438330","messageId":"patch-v3-3.3-8bde4e174b7-20211008T193041Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com","subject":"[PATCH v3 3/3] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-10-08T19:34:48Z","receivedAt":"2021-10-08T19:35:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line of:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 7a5355b4cf7..29859d3eebe 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -391,9 +391,12 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n+\t\ttimestamp_t tag_date = 0;\n \n-\t\tif (!parse_tag(tag) && tag->tag)\n+\t\tif (!parse_tag(tag) && tag->tag) {\n \t\t\ttag_tag = tag->tag;\n+\t\t\ttag_date = tag->date;\n+\t\t}\n \n \t\t/*\n \t\t * TRANSLATORS: This is a line of\n@@ -405,7 +408,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n+\t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.33.0.1492.g76eb1af92bc\n\n"},{"id":"441985","messageId":"cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com","subject":"[PATCH v2 0/3] object-name: make ambiguous object output translatable + show tag date","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T17:53:22Z","receivedAt":"2021-11-22T17:53:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This topic improves the output we emit on ambiguous objects as noted\nin 3/3, and makes it translatable. See [3] for v3.\n\nThe only changes since v3 are minor commit message improvements\nspotted while re-rolling this. I think revewers were happy with it in\nv3, but it fell through the cracks.\n\n1. https://lore.kernel.org/git/cover-v3-0.3-00000000000-20211008T193041Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (3):\n  object-name: remove unreachable \"unknown type\" handling\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n\n object-name.c | 68 +++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 61 insertions(+), 7 deletions(-)\n\nRange-diff against v3:\n1:  fb29e10ee35 ! 1:  2e7090c09f9 object-name: remove unreachable \"unknown type\" handling\n    @@ Metadata\n      ## Commit message ##\n         object-name: remove unreachable \"unknown type\" handling\n     \n    -    Remove the \"unknown type\" handling when displaying the ambiguous\n    -    object list. See [1] for the current output, and [1] for the commit\n    -    that added the \"unknown type\" handling.\n    +    Remove unreachable \"unknown type\" handling in the code that displays\n    +    the ambiguous object list. See [1] for the current output, and [1] for\n    +    the commit that added the \"unknown type\" handling.\n     \n         The reason this code wasn't reachable is because we're not passing in\n    -    OBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll just die in sort_ambiguous()\n    +    OBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll die in sort_ambiguous()\n         before we get to show_ambiguous_object():\n     \n             $ git rev-parse 8315\n2:  587a5717e47 ! 2:  00d84faeb1d object-name: make ambiguous object output translatable\n    @@ Commit message\n     \n         Change the output of show_ambiguous_object() added in [1] and last\n         tweaked in [2] and the preceding commit to be more friendly to\n    -    translators. By being able to customize the \"<SP><SP>%s\\n\" format\n    -    we're even ready for RTL languages, who'd presumably like to change\n    -    that to \"%s<SP><SP>\\n\".\n    +    translators.\n    +\n    +    By being able to customize the \"<SP><SP>%s\\n\" format we're even ready\n    +    for RTL languages, who'd presumably like to change that to\n    +    \"%s<SP><SP>\\n\".\n     \n         1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n            2016-09-26)\n3:  8bde4e174b7 = 3:  9d24bab635d object-name: show date for ambiguous tag objects\n-- \n2.34.0.822.gc64b680fd55\n\n"},{"id":"441986","messageId":"patch-v4-3.3-9d24bab635d-20211122T175219Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com","subject":"[PATCH v4 3/3] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T17:53:25Z","receivedAt":"2021-11-22T17:53:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line of:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 7a5355b4cf7..29859d3eebe 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -391,9 +391,12 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n+\t\ttimestamp_t tag_date = 0;\n \n-\t\tif (!parse_tag(tag) && tag->tag)\n+\t\tif (!parse_tag(tag) && tag->tag) {\n \t\t\ttag_tag = tag->tag;\n+\t\t\ttag_date = tag->date;\n+\t\t}\n \n \t\t/*\n \t\t * TRANSLATORS: This is a line of\n@@ -405,7 +408,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n+\t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.34.0.822.gc64b680fd55\n\n"},{"id":"441987","messageId":"patch-v4-1.3-2e7090c09f9-20211122T175219Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com","subject":"[PATCH v4 1/3] object-name: remove unreachable \"unknown type\" handling","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T17:53:23Z","receivedAt":"2021-11-22T17:53:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove unreachable \"unknown type\" handling in the code that displays\nthe ambiguous object list. See [1] for the current output, and [1] for\nthe commit that added the \"unknown type\" handling.\n\nThe reason this code wasn't reachable is because we're not passing in\nOBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll die in sort_ambiguous()\nbefore we get to show_ambiguous_object():\n\n    $ git rev-parse 8315\n    error: short object ID 8315 is ambiguous\n    hint: The candidates are:\n    fatal: invalid object type\n\nWe should do better here, but let's leave that for some future\nimprovement. In a subsequent commit I'll improve the output we do\nshow, and not having to handle the \"unknown type\" case simplifies that\nchange.\n\nEven though we know that this isn't reachable let's back that up with\nan assert() both for self-documentation and sanity checking.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..59e934262e7 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -376,8 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \n \tadvise(\"  %s %s%s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n-\t       desc.buf);\n+\t       type_name(type), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.34.0.822.gc64b680fd55\n\n"},{"id":"441988","messageId":"patch-v4-2.3-00d84faeb1d-20211122T175219Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com","subject":"[PATCH v4 2/3] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-22T17:53:24Z","receivedAt":"2021-11-22T17:53:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators.\n\nBy being able to customize the \"<SP><SP>%s\\n\" format we're even ready\nfor RTL languages, who'd presumably like to change that to\n\"%s<SP><SP>\\n\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 53 insertions(+), 5 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 59e934262e7..7a5355b4cf7 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,6 +356,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n@@ -363,22 +364,69 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n+\n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf ad = STRBUF_INIT;\n+\t\tstruct strbuf s = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n+\n+\t\tstrbuf_release(&ad);\n+\t\tstrbuf_release(&s);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n+\t\tconst char *tag_tag = \"\";\n+\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_tag = tag->tag;\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t} else {\n+\t\tBUG(\"unreachable\");\n \t}\n \n-\tadvise(\"  %s %s%s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type), desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output,\n+\t * translated above.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.34.0.822.gc64b680fd55\n\n"},{"id":"442048","messageId":"YZwbphPpfGk78w2f@coredump.intra.peff.net","threadId":"56641","inReplyTo":"patch-v4-1.3-2e7090c09f9-20211122T175219Z-avarab@gmail.com","subject":"Re: [PATCH v4 1/3] object-name: remove unreachable \"unknown type\" handling","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-11-22T22:37:26Z","receivedAt":"2021-11-22T22:37:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Nov 22, 2021 at 06:53:23PM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> Remove unreachable \"unknown type\" handling in the code that displays\n> the ambiguous object list. See [1] for the current output, and [1] for\n> the commit that added the \"unknown type\" handling.\n> \n> The reason this code wasn't reachable is because we're not passing in\n> OBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll die in sort_ambiguous()\n> before we get to show_ambiguous_object():\n> \n>     $ git rev-parse 8315\n>     error: short object ID 8315 is ambiguous\n>     hint: The candidates are:\n>     fatal: invalid object type\n\nI'm not so sure about this reasoning. In the code we are getting the\ntype fresh from oid_object_info():\n\n> @@ -361,6 +361,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t\treturn 0;\n>  \n>  \ttype = oid_object_info(ds->repo, oid, NULL);\n> +\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n> +\t       type == OBJ_BLOB || type == OBJ_TAG);\n\nso at the very least we have to worry about the answer changing between\nthe two spots. You talk above about ALLOW_UNKNOWN_TYPE, but can't we\njust get a straight \"-1\" if there's an error opening the object?\n\nI'm also confused about the mention of die in sort_ambiguous(). It looks\nlike it would just produce a funny sort order in that case.\n\nHere's a case that triggers the difference:\n\n  git init repo\n  cd repo\n\n  one=$(echo 851 | git hash-object -w --stdin)\n  two=$(echo 872 | git hash-object -w --stdin)\n  oid=$(echo $two | cut -c1-4)\n\n  fn=.git/objects/$(echo $two | perl -pe 's{..}{$&/}')\n  chmod +w $fn\n  echo broken >$fn\n\n  git show $oid\n\nWithout your patch, it produces:\n\n  error: short object ID ee3d is ambiguous\n  hint: The candidates are:\n  error: inflate: data stream error (incorrect header check)\n  error: unable to unpack ee3d8abaa95a7395b373892b2593de2f426814e2 header\n  error: inflate: data stream error (incorrect header check)\n  error: unable to unpack ee3d8abaa95a7395b373892b2593de2f426814e2 header\n  hint:   ee3d8ab unknown type\n  hint:   ee3de99 blob\n\nWith your patch:\n\n  error: short object ID ee3d is ambiguous\n  hint: The candidates are:\n  error: inflate: data stream error (incorrect header check)\n  error: unable to unpack ee3d8abaa95a7395b373892b2593de2f426814e2 header\n  error: inflate: data stream error (incorrect header check)\n  error: unable to unpack ee3d8abaa95a7395b373892b2593de2f426814e2 header\n  git: object-name.c:364: show_ambiguous_object: Assertion `type == OBJ_TREE || type == OBJ_COMMIT || type == OBJ_BLOB || type == OBJ_TAG' failed.\n  Aborted\n\n-Peff\n"},{"id":"442323","messageId":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com","subject":"[PATCH v5 0/6] object-name: make ambiguous object output translatable + show tag date","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:38Z","receivedAt":"2021-11-25T22:06:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This topic improves the output we emit on ambiguous objects as noted\nin 4/6, and makes it translatable, see 3/6. See [1] for v4.\n\nThis addresses the feedback Jeff King had on v4. There weren't any\ntests for cases where we'd return -1 when parsing objects, and I was\nfocused on different object types in earlier iterations, and missed\nthat case.\n\nSo this v5 leads with some exhaustive testing of the existing\nfunctionality to address that and other blind spots,\n\nI then resurrected the patch from an earlier iteration to buffer the\noutput for a single advice() call at the end. As the exhaustive tests\nthat we have now show if we call error() (which can and will happen\nseveral times on invalid objects) while parsing our N objects, we'll\nsplit up the header and body for the advice(), by buffering it up\nwe're guaranteed to print errors and the payload separately.\n\n1. https://lore.kernel.org/git/cover-v4-0.3-00000000000-20211122T175219Z-avarab@gmail.com\n\nÆvar Arnfjörð Bjarmason (6):\n  object-name tests: add tests for ambiguous object blind spots\n  object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n  object-name: iterate ambiguous objects before showing header\n  object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n\n object-name.c                       | 111 +++++++++++++++++++++++++---\n t/t1512-rev-parse-disambiguation.sh |  83 +++++++++++++++++++++\n 2 files changed, 182 insertions(+), 12 deletions(-)\n\nRange-diff against v4:\n-:  ----------- > 1:  767165d096d object-name tests: add tests for ambiguous object blind spots\n1:  2e7090c09f9 ! 2:  ee86912f1c1 object-name: remove unreachable \"unknown type\" handling\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    object-name: remove unreachable \"unknown type\" handling\n    +    object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n     \n    -    Remove unreachable \"unknown type\" handling in the code that displays\n    -    the ambiguous object list. See [1] for the current output, and [1] for\n    -    the commit that added the \"unknown type\" handling.\n    +    Amend the \"unknown type\" handling in the code that displays the\n    +    ambiguous object list to assert() that we're either going to get the\n    +    \"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\n    +    return value from oid_object_info().\n     \n    -    The reason this code wasn't reachable is because we're not passing in\n    -    OBJECT_INFO_ALLOW_UNKNOWN_TYPE, so we'll die in sort_ambiguous()\n    -    before we get to show_ambiguous_object():\n    +    See [1] for the current output, and [1] for the commit that added the\n    +    \"unknown type\" handling.\n     \n    -        $ git rev-parse 8315\n    -        error: short object ID 8315 is ambiguous\n    -        hint: The candidates are:\n    -        fatal: invalid object type\n    +    We are never going to get an \"unknown type\" in the sense of custom\n    +    types crafted with \"hash-object --literally\", since we're not using\n    +    the OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n     \n    -    We should do better here, but let's leave that for some future\n    -    improvement. In a subsequent commit I'll improve the output we do\n    -    show, and not having to handle the \"unknown type\" case simplifies that\n    -    change.\n    +    If we manage to otherwise unpack such an object without errors we'll\n    +    die() in parse_loose_header_extended() called by sort_ambiguous()\n    +    before we get to show_ambiguous_object(), as is asserted by the test\n    +    added in the preceding commit.\n     \n    -    Even though we know that this isn't reachable let's back that up with\n    -    an assert() both for self-documentation and sanity checking.\n    +    So saying \"unknown type\" here was always misleading, we really meant\n    +    to say that we had a failure parsing the object at all, if the problem\n    +    is only that it's type is unknown we won't reach this code.\n    +\n    +    So let's emit a generic \"[bad object]\" instead. As our tests added in\n    +    the preceding commit show, we'll have emitted various \"error\" output\n    +    already in those cases.\n    +\n    +    We should do better in the truly \"unknown type\" cases, which we'd need\n    +    to handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\n    +    flag. But let's leave that for some future improvement. In a\n    +    subsequent commit I'll improve the output we do show, and not having\n    +    to handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\n    +    simplifies that change.\n     \n         1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n            then SHA-1, 2018-05-10)\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \t\treturn 0;\n      \n      \ttype = oid_object_info(ds->repo, oid, NULL);\n    ++\n    ++\tif (type < 0) {\n    ++\t\tstrbuf_addstr(&desc, \"[bad object]\");\n    ++\t\tgoto out;\n    ++\t}\n    ++\n     +\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n     +\t       type == OBJ_BLOB || type == OBJ_TAG);\n    ++\tstrbuf_addstr(&desc, type_name(type));\n    ++\n      \tif (type == OBJ_COMMIT) {\n      \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n      \t\tif (commit) {\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    + \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    + \t}\n      \n    - \tadvise(\"  %s %s%s\",\n    +-\tadvise(\"  %s %s%s\",\n    ++out:\n    ++\tadvise(\"  %s %s\",\n      \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n     -\t       type_name(type) ? type_name(type) : \"unknown type\",\n    --\t       desc.buf);\n    -+\t       type_name(type), desc.buf);\n    + \t       desc.buf);\n      \n      \tstrbuf_release(&desc);\n    - \treturn 0;\n    +\n    + ## t/t1512-rev-parse-disambiguation.sh ##\n    +@@ t/t1512-rev-parse-disambiguation.sh: test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n    + \terror: unable to unpack cafe... header\n    + \terror: inflate: data stream error (incorrect header check)\n    + \terror: unable to unpack cafe... header\n    +-\thint:   cafe... unknown type\n    ++\thint:   cafe... [bad object]\n    + \thint:   cafe... blob\n    + \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n    + \tUse '\\''--'\\'' to separate paths from revisions, like this:\n2:  00d84faeb1d ! 3:  b79964483e8 object-name: make ambiguous object output translatable\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \n      \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n      \t\treturn 0;\n    -@@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    + \n    ++\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n      \ttype = oid_object_info(ds->repo, oid, NULL);\n    + \n    + \tif (type < 0) {\n    +-\t\tstrbuf_addstr(&desc, \"[bad object]\");\n    ++\t\t/*\n    ++\t\t * TRANSLATORS: This is a line of ambiguous object\n    ++\t\t * output shown when we cannot look up or parse the\n    ++\t\t * object in question. E.g. \"deadbeef [bad object]\".\n    ++\t\t */\n    ++\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n    + \t\tgoto out;\n    + \t}\n    + \n      \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n      \t       type == OBJ_BLOB || type == OBJ_TAG);\n    -+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n    -+\n    +-\tstrbuf_addstr(&desc, type_name(type));\n    + \n      \tif (type == OBJ_COMMIT) {\n     +\t\tstruct strbuf ad = STRBUF_INIT;\n     +\t\tstruct strbuf s = STRBUF_INIT;\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\t * object output. E.g. \"deadbeef blob\".\n     +\t\t */\n     +\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n    -+\t} else {\n    -+\t\tBUG(\"unreachable\");\n      \t}\n      \n    --\tadvise(\"  %s %s%s\",\n    ++\n    + out:\n    +-\tadvise(\"  %s %s\",\n     -\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n    --\t       type_name(type), desc.buf);\n    +-\t       desc.buf);\n     +\t/*\n    -+\t * TRANSLATORS: This is line item of ambiguous object output,\n    -+\t * translated above.\n    ++\t * TRANSLATORS: This is line item of ambiguous object output\n    ++\t * from describe_ambiguous_object() above.\n     +\t */\n     +\tadvise(_(\"  %s\"), desc.buf);\n      \n3:  9d24bab635d ! 4:  36b6b440c37 object-name: show date for ambiguous tag objects\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \n      \t\t/*\n      \t\t * TRANSLATORS: This is a line of\n    -@@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    + \t\t * ambiguous tag object output. E.g.:\n    + \t\t *\n    +-\t\t *    \"deadbeef tag Some Tag Message\"\n    ++\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n    + \t\t *\n    + \t\t * The second argument is the \"tag\" string from\n      \t\t * object.c, it should (hopefully) already be\n      \t\t * translated.\n      \t\t */\n-:  ----------- > 5:  8880c283559 object-name: iterate ambiguous objects before showing header\n-:  ----------- > 6:  78bb0995f08 object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442324","messageId":"patch-v5-1.6-767165d096d-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:39Z","receivedAt":"2021-11-25T22:06:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the tests for ambiguous objects to check how we handle objects\nwhere we return OBJ_BAD when trying to parse them. As noted in [1] we\nhave a blindspot when it comes to this behavior.\n\nSince we need to add new test data here let's extend these tests to be\ntested under SHA-256, in d7a2fc82491 (t1512: skip test if not using\nSHA-1, 2018-05-13) all of the existing tests were skipped, as they\nrely on specific SHA-1 object IDs.\n\nFor these tests it only matters that the first 4 characters of the OID\nprefix are the same for both SHA-1 and SHA-256. This uses strings that\nI mined, and have the same prefix when hashed with both.\n\n1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1512-rev-parse-disambiguation.sh | 84 +++++++++++++++++++++++++++++\n 1 file changed, 84 insertions(+)\n\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 7891a6becf3..ae1c0cf2b21 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -25,6 +25,90 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+test_cmp_failed_rev_parse () {\n+\tdir=$1\n+\trev=$2\n+\tshift\n+\n+\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n+\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../g\" <actual.raw >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ambiguous blob output' '\n+\tgit init --bare blob.prefix &&\n+\t(\n+\t\tcd blob.prefix &&\n+\n+\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n+\t\techo brocdnra | git hash-object -w --stdin &&\n+\t\techo brigddsv | git hash-object -w --stdin &&\n+\n+\t\t# Both start with \"beef..\"\n+\t\techo 1agllotbh | git hash-object -w --stdin &&\n+\t\techo 1bbfctrkc | git hash-object -w --stdin\n+\t) &&\n+\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID beef... is ambiguous\n+\thint: The candidates are:\n+\thint:   beef... blob\n+\thint:   beef... blob\n+\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.prefix beef\n+'\n+\n+test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n+\tgit init --bare blob.bad &&\n+\t(\n+\t\tcd blob.bad &&\n+\n+\t\t# Both have the prefix \"bad0\"\n+\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n+\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n+\t) &&\n+\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID bad0... is ambiguous\n+\thint: The candidates are:\n+\tfatal: invalid object type\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.bad bad0\n+'\n+\n+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n+\tgit init --bare blob.corrupt &&\n+\t(\n+\t\tcd blob.corrupt &&\n+\n+\t\t# Both have the prefix \"cafe\"\n+\t\techo bnkxmdwz | git hash-object -w --stdin &&\n+\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n+\n+\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n+\t\tchmod 755 $oidf &&\n+\t\techo broken >$oidf\n+\t) &&\n+\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID cafe... is ambiguous\n+\thint: The candidates are:\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\thint:   cafe... unknown type\n+\thint:   cafe... blob\n+\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.corrupt cafe\n+'\n+\n if ! test_have_prereq SHA1\n then\n \tskip_all='not using SHA-1 for objects'\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442325","messageId":"patch-v5-2.6-ee86912f1c1-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 2/6] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:40Z","receivedAt":"2021-11-25T22:07:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the \"unknown type\" handling in the code that displays the\nambiguous object list to assert() that we're either going to get the\n\"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\nreturn value from oid_object_info().\n\nSee [1] for the current output, and [1] for the commit that added the\n\"unknown type\" handling.\n\nWe are never going to get an \"unknown type\" in the sense of custom\ntypes crafted with \"hash-object --literally\", since we're not using\nthe OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n\nIf we manage to otherwise unpack such an object without errors we'll\ndie() in parse_loose_header_extended() called by sort_ambiguous()\nbefore we get to show_ambiguous_object(), as is asserted by the test\nadded in the preceding commit.\n\nSo saying \"unknown type\" here was always misleading, we really meant\nto say that we had a failure parsing the object at all, if the problem\nis only that it's type is unknown we won't reach this code.\n\nSo let's emit a generic \"[bad object]\" instead. As our tests added in\nthe preceding commit show, we'll have emitted various \"error\" output\nalready in those cases.\n\nWe should do better in the truly \"unknown type\" cases, which we'd need\nto handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\nflag. But let's leave that for some future improvement. In a\nsubsequent commit I'll improve the output we do show, and not having\nto handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\nsimplifies that change.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 14 ++++++++++++--\n t/t1512-rev-parse-disambiguation.sh |  2 +-\n 2 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..9750634ee76 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,16 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\n+\tif (type < 0) {\n+\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\tgoto out;\n+\t}\n+\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n+\tstrbuf_addstr(&desc, type_name(type));\n+\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -374,9 +384,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n \t}\n \n-\tadvise(\"  %s %s%s\",\n+out:\n+\tadvise(\"  %s %s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n \t       desc.buf);\n \n \tstrbuf_release(&desc);\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex ae1c0cf2b21..f1948980dff 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -100,7 +100,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n-\thint:   cafe... unknown type\n+\thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n \tUse '\\''--'\\'' to separate paths from revisions, like this:\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442326","messageId":"patch-v5-3.6-b79964483e8-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 3/6] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:41Z","receivedAt":"2021-11-25T22:07:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators.\n\nBy being able to customize the \"<SP><SP>%s\\n\" format we're even ready\nfor RTL languages, who'd presumably like to change that to\n\"%s<SP><SP>\\n\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 64 +++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 57 insertions(+), 7 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 9750634ee76..1dcbba7fa76 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,38 +356,88 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n \n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \n \tif (type < 0) {\n-\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous object\n+\t\t * output shown when we cannot look up or parse the\n+\t\t * object in question. E.g. \"deadbeef [bad object]\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n-\tstrbuf_addstr(&desc, type_name(type));\n \n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf ad = STRBUF_INIT;\n+\t\tstruct strbuf s = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n+\n+\t\tstrbuf_release(&ad);\n+\t\tstrbuf_release(&s);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n+\t\tconst char *tag_tag = \"\";\n+\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_tag = tag->tag;\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n \t}\n \n+\n out:\n-\tadvise(\"  %s %s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output\n+\t * from describe_ambiguous_object() above.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442327","messageId":"patch-v5-4.6-36b6b440c37-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 4/6] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:42Z","receivedAt":"2021-11-25T22:07:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line of:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 1dcbba7fa76..707480ed191 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -402,21 +402,26 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n+\t\ttimestamp_t tag_date = 0;\n \n-\t\tif (!parse_tag(tag) && tag->tag)\n+\t\tif (!parse_tag(tag) && tag->tag) {\n \t\t\ttag_tag = tag->tag;\n+\t\t\ttag_date = tag->date;\n+\t\t}\n \n \t\t/*\n \t\t * TRANSLATORS: This is a line of\n \t\t * ambiguous tag object output. E.g.:\n \t\t *\n-\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n \t\t *\n \t\t * The second argument is the \"tag\" string from\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n+\t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442328","messageId":"patch-v5-5.6-8880c283559-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 5/6] object-name: iterate ambiguous objects before showing header","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:43Z","receivedAt":"2021-11-25T22:07:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"The candidates are\" header that's shown for ambiguous\nobjects to be shown after we've iterated over all of the objects.\n\nIf we get any errors while doing so we don't want to split up the the\nheader and the list as a result. The two will now be printed together,\nas shown in the updated testcase.\n\nAs we're accumulating the lines into as \"struct strbuf\" before\nemitting them we need to add a trailing newline to the call in\nshow_ambiguous_object(). This and the change from \"The candidates\nare:\" to \"The candidates are:\\n%s\" helps to give translators more\ncontext.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 27 +++++++++++++++++++++++----\n t/t1512-rev-parse-disambiguation.sh |  3 +--\n 2 files changed, 24 insertions(+), 6 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 707480ed191..fd8b9244b5e 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -351,9 +351,16 @@ static int init_object_disambiguation(struct repository *r,\n \treturn 0;\n }\n \n+struct ambiguous_output {\n+\tconst struct disambiguate_state *ds;\n+\tstruct strbuf advice;\n+};\n+\n static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n-\tconst struct disambiguate_state *ds = data;\n+\tstruct ambiguous_output *state = data;\n+\tconst struct disambiguate_state *ds = state->ds;\n+\tstruct strbuf *advice = &state->advice;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n \tconst char *hash;\n@@ -442,7 +449,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * TRANSLATORS: This is line item of ambiguous object output\n \t * from describe_ambiguous_object() above.\n \t */\n-\tadvise(_(\"  %s\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n@@ -541,6 +548,10 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n+\t\tstruct ambiguous_output out = {\n+\t\t\t.ds = &ds,\n+\t\t\t.advice = STRBUF_INIT,\n+\t\t};\n \n \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n \n@@ -553,13 +564,21 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tif (!ds.ambiguous)\n \t\t\tds.fn = NULL;\n \n-\t\tadvise(_(\"The candidates are:\"));\n \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n \t\tsort_ambiguous_oid_array(r, &collect);\n \n-\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &out))\n \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n+\n+\t\t/*\n+\t\t * TRANSLATORS: The argument is the list of ambiguous\n+\t\t * objects composed in show_ambiguous_object(). See\n+\t\t * its \"TRANSLATORS\" comments for details.\n+\t\t */\n+\t\tadvise(_(\"The candidates are:\\n%s\"), out.advice.buf);\n+\n \t\toid_array_clear(&collect);\n+\t\tstrbuf_release(&out.advice);\n \t}\n \n \treturn status;\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex f1948980dff..9e67231cdbf 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -73,7 +73,6 @@ test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n \n \tcat >expect <<-\\EOF &&\n \terror: short object ID bad0... is ambiguous\n-\thint: The candidates are:\n \tfatal: invalid object type\n \tEOF\n \ttest_cmp_failed_rev_parse blob.bad bad0\n@@ -95,11 +94,11 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \n \tcat >expect <<-\\EOF &&\n \terror: short object ID cafe... is ambiguous\n-\thint: The candidates are:\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n+\thint: The candidates are:\n \thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"442329","messageId":"patch-v5-6.6-78bb0995f08-20211125T215529Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v5 6/6] object-name: re-use \"struct strbuf\" in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-11-25T22:03:44Z","receivedAt":"2021-11-25T22:07:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reduce the allocations done by show_ambiguous_object() by moving the\n\"desc\" strbuf into the \"struct ambiguous_output\" introduced in the\npreceding commit.\n\nThis doesn't matter for optimization purposes, but since we're\naccumulating a \"struct strbuf advice\" anyway let's follow that pattern\nand add a \"struct strbuf sb\", we can then strbuf_reset() it rather\nthan calling strbuf_release() for each call to\nshow_ambiguous_object().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fd8b9244b5e..f96552e7af7 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -354,6 +354,7 @@ static int init_object_disambiguation(struct repository *r,\n struct ambiguous_output {\n \tconst struct disambiguate_state *ds;\n \tstruct strbuf advice;\n+\tstruct strbuf sb;\n };\n \n static int show_ambiguous_object(const struct object_id *oid, void *data)\n@@ -361,7 +362,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tstruct ambiguous_output *state = data;\n \tconst struct disambiguate_state *ds = state->ds;\n \tstruct strbuf *advice = &state->advice;\n-\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct strbuf *sb = &state->sb;\n \tint type;\n \tconst char *hash;\n \n@@ -377,7 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * output shown when we cannot look up or parse the\n \t\t * object in question. E.g. \"deadbeef [bad object]\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n@@ -402,7 +403,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t *\n \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n+\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n \n \t\tstrbuf_release(&ad);\n \t\tstrbuf_release(&s);\n@@ -426,7 +427,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n \t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n \t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n@@ -434,13 +435,13 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef tree\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s tree\"), hash);\n \t} else if (type == OBJ_BLOB) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef blob\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s blob\"), hash);\n \t}\n \n \n@@ -449,9 +450,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * TRANSLATORS: This is line item of ambiguous object output\n \t * from describe_ambiguous_object() above.\n \t */\n-\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), sb->buf);\n \n-\tstrbuf_release(&desc);\n+\tstrbuf_reset(sb);\n \treturn 0;\n }\n \n@@ -550,6 +551,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n \t\tstruct ambiguous_output out = {\n \t\t\t.ds = &ds,\n+\t\t\t.sb = STRBUF_INIT,\n \t\t\t.advice = STRBUF_INIT,\n \t\t};\n \n@@ -579,6 +581,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \t\toid_array_clear(&collect);\n \t\tstrbuf_release(&out.advice);\n+\t\tstrbuf_release(&out.sb);\n \t}\n \n \treturn status;\n-- \n2.34.1.838.g779e9098efb\n\n"},{"id":"444881","messageId":"YcTvbH9tjXoJYpVN@google.com","threadId":"56641","inReplyTo":"patch-v5-1.6-767165d096d-20211125T215529Z-avarab@gmail.com","subject":"Re: [PATCH v5 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2021-12-23T21:51:40Z","receivedAt":"2021-12-23T21:51:51Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2021.11.25 23:03, Ævar Arnfjörð Bjarmason wrote:\n> Extend the tests for ambiguous objects to check how we handle objects\n> where we return OBJ_BAD when trying to parse them. As noted in [1] we\n> have a blindspot when it comes to this behavior.\n> \n> Since we need to add new test data here let's extend these tests to be\n> tested under SHA-256, in d7a2fc82491 (t1512: skip test if not using\n> SHA-1, 2018-05-13) all of the existing tests were skipped, as they\n> rely on specific SHA-1 object IDs.\n> \n> For these tests it only matters that the first 4 characters of the OID\n> prefix are the same for both SHA-1 and SHA-256. This uses strings that\n> I mined, and have the same prefix when hashed with both.\n> \n> 1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t1512-rev-parse-disambiguation.sh | 84 +++++++++++++++++++++++++++++\n>  1 file changed, 84 insertions(+)\n> \n> diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\n> index 7891a6becf3..ae1c0cf2b21 100755\n> --- a/t/t1512-rev-parse-disambiguation.sh\n> +++ b/t/t1512-rev-parse-disambiguation.sh\n> @@ -25,6 +25,90 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>  \n>  . ./test-lib.sh\n>  \n> +test_cmp_failed_rev_parse () {\n> +\tdir=$1\n> +\trev=$2\n> +\tshift\n> +\n> +\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n> +\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../g\" <actual.raw >actual &&\n> +\ttest_cmp expect actual\n> +}\n> +\n> +test_expect_success 'ambiguous blob output' '\n> +\tgit init --bare blob.prefix &&\n> +\t(\n> +\t\tcd blob.prefix &&\n> +\n> +\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n> +\t\techo brocdnra | git hash-object -w --stdin &&\n> +\t\techo brigddsv | git hash-object -w --stdin &&\n\nThese \"dead..\" objects don't seem to be used later, unless I've missed\nsomething.\n\n\n> +\t\t# Both start with \"beef..\"\n> +\t\techo 1agllotbh | git hash-object -w --stdin &&\n> +\t\techo 1bbfctrkc | git hash-object -w --stdin\n> +\t) &&\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID beef... is ambiguous\n> +\thint: The candidates are:\n> +\thint:   beef... blob\n> +\thint:   beef... blob\n> +\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n> +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n> +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.prefix beef\n> +'\n\nRather than comparing the entire output (which can be brittle), can we\njust grep for the important parts of the error message and compare\nthose?\n\n\n> +test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n> +\tgit init --bare blob.bad &&\n> +\t(\n> +\t\tcd blob.bad &&\n> +\n> +\t\t# Both have the prefix \"bad0\"\n> +\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n> +\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n> +\t) &&\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID bad0... is ambiguous\n> +\thint: The candidates are:\n> +\tfatal: invalid object type\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.bad bad0\n> +'\n> +\n> +test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n> +\tgit init --bare blob.corrupt &&\n> +\t(\n> +\t\tcd blob.corrupt &&\n> +\n> +\t\t# Both have the prefix \"cafe\"\n> +\t\techo bnkxmdwz | git hash-object -w --stdin &&\n> +\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n> +\n> +\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n> +\t\tchmod 755 $oidf &&\n> +\t\techo broken >$oidf\n> +\t) &&\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID cafe... is ambiguous\n> +\thint: The candidates are:\n> +\terror: inflate: data stream error (incorrect header check)\n> +\terror: unable to unpack cafe... header\n> +\terror: inflate: data stream error (incorrect header check)\n> +\terror: unable to unpack cafe... header\n> +\thint:   cafe... unknown type\n> +\thint:   cafe... blob\n> +\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n> +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n> +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.corrupt cafe\n> +'\n> +\n>  if ! test_have_prereq SHA1\n>  then\n>  \tskip_all='not using SHA-1 for objects'\n> -- \n> 2.34.1.838.g779e9098efb\n> \n"},{"id":"444882","messageId":"YcTvfgxYc7U7bodg@google.com","threadId":"56641","inReplyTo":"patch-v5-2.6-ee86912f1c1-20211125T215529Z-avarab@gmail.com","subject":"Re: [PATCH v5 2/6] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2021-12-23T21:51:58Z","receivedAt":"2021-12-23T21:52:07Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2021.11.25 23:03, Ævar Arnfjörð Bjarmason wrote:\n> Amend the \"unknown type\" handling in the code that displays the\n> ambiguous object list to assert() that we're either going to get the\n> \"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\n> return value from oid_object_info().\n> \n> See [1] for the current output, and [1] for the commit that added the\n> \"unknown type\" handling.\n> \n> We are never going to get an \"unknown type\" in the sense of custom\n> types crafted with \"hash-object --literally\", since we're not using\n> the OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n> \n> If we manage to otherwise unpack such an object without errors we'll\n> die() in parse_loose_header_extended() called by sort_ambiguous()\n> before we get to show_ambiguous_object(), as is asserted by the test\n> added in the preceding commit.\n> \n> So saying \"unknown type\" here was always misleading, we really meant\n> to say that we had a failure parsing the object at all, if the problem\n> is only that it's type is unknown we won't reach this code.\n\nAre there situations other than repo corruption where this could happen?\nMaybe it would be more useful to just die() at this point and give the\nuser advice on how to investigate / fix the corruption, rather than\ntrying to disambiguate the objects involved.\n\n\n> So let's emit a generic \"[bad object]\" instead. As our tests added in\n> the preceding commit show, we'll have emitted various \"error\" output\n> already in those cases.\n> \n> We should do better in the truly \"unknown type\" cases, which we'd need\n> to handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\n> flag. But let's leave that for some future improvement. In a\n> subsequent commit I'll improve the output we do show, and not having\n> to handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\n> simplifies that change.\n> \n> 1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n>    then SHA-1, 2018-05-10)\n> 2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n>    2016-09-26)\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  object-name.c                       | 14 ++++++++++++--\n>  t/t1512-rev-parse-disambiguation.sh |  2 +-\n>  2 files changed, 13 insertions(+), 3 deletions(-)\n> \n> diff --git a/object-name.c b/object-name.c\n> index fdff4601b2c..9750634ee76 100644\n> --- a/object-name.c\n> +++ b/object-name.c\n> @@ -361,6 +361,16 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t\treturn 0;\n>  \n>  \ttype = oid_object_info(ds->repo, oid, NULL);\n> +\n> +\tif (type < 0) {\n> +\t\tstrbuf_addstr(&desc, \"[bad object]\");\n> +\t\tgoto out;\n> +\t}\n> +\n> +\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n> +\t       type == OBJ_BLOB || type == OBJ_TAG);\n> +\tstrbuf_addstr(&desc, type_name(type));\n> +\n>  \tif (type == OBJ_COMMIT) {\n>  \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n>  \t\tif (commit) {\n> @@ -374,9 +384,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n>  \t}\n>  \n> -\tadvise(\"  %s %s%s\",\n> +out:\n> +\tadvise(\"  %s %s\",\n>  \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n> -\t       type_name(type) ? type_name(type) : \"unknown type\",\n>  \t       desc.buf);\n>  \n>  \tstrbuf_release(&desc);\n> diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\n> index ae1c0cf2b21..f1948980dff 100755\n> --- a/t/t1512-rev-parse-disambiguation.sh\n> +++ b/t/t1512-rev-parse-disambiguation.sh\n> @@ -100,7 +100,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n>  \terror: unable to unpack cafe... header\n>  \terror: inflate: data stream error (incorrect header check)\n>  \terror: unable to unpack cafe... header\n> -\thint:   cafe... unknown type\n> +\thint:   cafe... [bad object]\n>  \thint:   cafe... blob\n>  \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n>  \tUse '\\''--'\\'' to separate paths from revisions, like this:\n> -- \n> 2.34.1.838.g779e9098efb\n> \n"},{"id":"444883","messageId":"ec6a20a3d694d1d7e3db14f6bab42aff3e82c135.1640295389.git.steadmon@google.com","threadId":"56641","inReplyTo":"patch-v5-3.6-b79964483e8-20211125T215529Z-avarab@gmail.com","subject":"[PATCH] fixup! object-name: make ambiguous object output translatable","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2021-12-23T21:54:53Z","receivedAt":"2021-12-23T21:54:58Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"A nitpick, but the \"ad\" and \"s\" strbuf names here are not very friendly\nfor readers who don't know offhand what the format_commit_message fields\nexpand to. This makes them more self-descriptive.\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n object-name.c | 15 ++++++++-------\n 1 file changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 1dcbba7fa7..dcf3ab9999 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -378,15 +378,15 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n \n \tif (type == OBJ_COMMIT) {\n-\t\tstruct strbuf ad = STRBUF_INIT;\n-\t\tstruct strbuf s = STRBUF_INIT;\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tstruct strbuf msg = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n-\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n \t\t}\n \n \t\t/*\n@@ -395,10 +395,11 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t *\n \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n+\t\t\t    hash, date.buf, msg.buf);\n \n-\t\tstrbuf_release(&ad);\n-\t\tstrbuf_release(&s);\n+\t\tstrbuf_release(&date);\n+\t\tstrbuf_release(&msg);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n\nbase-commit: ea5019ecd7a405d7d5f6527054d0aaca2d3b4bcd\n-- \n2.34.1.448.ga2b2bfdf31-goog\n\n"},{"id":"444886","messageId":"xmqqr1a36j0d.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v5-2.6-ee86912f1c1-20211125T215529Z-avarab@gmail.com","subject":"Re: [PATCH v5 2/6] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-23T22:42:10Z","receivedAt":"2021-12-23T22:42:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\n> index ae1c0cf2b21..f1948980dff 100755\n> --- a/t/t1512-rev-parse-disambiguation.sh\n> +++ b/t/t1512-rev-parse-disambiguation.sh\n> @@ -100,7 +100,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n>  \terror: unable to unpack cafe... header\n>  \terror: inflate: data stream error (incorrect header check)\n>  \terror: unable to unpack cafe... header\n> -\thint:   cafe... unknown type\n> +\thint:   cafe... [bad object]\n>  \thint:   cafe... blob\n>  \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n>  \tUse '\\''--'\\'' to separate paths from revisions, like this:\n\nOK.\n"},{"id":"444887","messageId":"xmqqmtkr6iq8.fsf@gitster.g","threadId":"56641","inReplyTo":"ec6a20a3d694d1d7e3db14f6bab42aff3e82c135.1640295389.git.steadmon@google.com","subject":"Re: [PATCH] fixup! object-name: make ambiguous object output translatable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-23T22:48:15Z","receivedAt":"2021-12-23T22:48:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> A nitpick, but the \"ad\" and \"s\" strbuf names here are not very friendly\n> for readers who don't know offhand what the format_commit_message fields\n> expand to. This makes them more self-descriptive.\n\nSounds like a sensible change.  It seems that this thread didn't\nhave gathered much interest by others (not many review comments), or\nby its author (not an ack to a suggestion like this), so perhaps I\nshould put on a cold storage and expect an update when the list is a\nbit more quiescent.\n\nThanks.\n\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n>  object-name.c | 15 ++++++++-------\n>  1 file changed, 8 insertions(+), 7 deletions(-)\n>\n> diff --git a/object-name.c b/object-name.c\n> index 1dcbba7fa7..dcf3ab9999 100644\n> --- a/object-name.c\n> +++ b/object-name.c\n> @@ -378,15 +378,15 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t       type == OBJ_BLOB || type == OBJ_TAG);\n>  \n>  \tif (type == OBJ_COMMIT) {\n> -\t\tstruct strbuf ad = STRBUF_INIT;\n> -\t\tstruct strbuf s = STRBUF_INIT;\n> +\t\tstruct strbuf date = STRBUF_INIT;\n> +\t\tstruct strbuf msg = STRBUF_INIT;\n>  \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n>  \n>  \t\tif (commit) {\n>  \t\t\tstruct pretty_print_context pp = {0};\n>  \t\t\tpp.date_mode.type = DATE_SHORT;\n> -\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n> -\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n> +\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n> +\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n>  \t\t}\n>  \n>  \t\t/*\n> @@ -395,10 +395,11 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t\t *\n>  \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n>  \t\t */\n> -\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n> +\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n> +\t\t\t    hash, date.buf, msg.buf);\n>  \n> -\t\tstrbuf_release(&ad);\n> -\t\tstrbuf_release(&s);\n> +\t\tstrbuf_release(&date);\n> +\t\tstrbuf_release(&msg);\n>  \t} else if (type == OBJ_TAG) {\n>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>  \t\tconst char *tag_tag = \"\";\n>\n> base-commit: ea5019ecd7a405d7d5f6527054d0aaca2d3b4bcd\n"},{"id":"445079","messageId":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com","subject":"[PATCH v6 0/6] object-name: make ambiguous object output translatable + show tag date","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:34:56Z","receivedAt":"2021-12-28T14:35:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This topic improves the output we emit on ambiguous objects as noted\nin 4/6, and makes it translatable, see 3/6. See [1] for v5.\n\nThis iteration addresses various small feedback from Josh\nSteadmon. I've incorporated a variable rename fixups here, and\nhopefully answered small questions on the v5 thread with amended\ncommit messages.\n\nFor the case of \"dead\" prefixed objects being unused but \"beef\" being\nused I just added a test for the \"dead\" objects. They're not strictly\nneeded, but having them for the \"dead...beef\" symetry and for use in\nfuture tests is probably better, so I kept them in.\n\n1. http://lore.kernel.org/git/cover-v5-0.6-00000000000-20211125T215529Z-avarab@gmail.com\n\nÆvar Arnfjörð Bjarmason (6):\n  object-name tests: add tests for ambiguous object blind spots\n  object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n  object-name: iterate ambiguous objects before showing header\n  object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n\n object-name.c                       | 112 +++++++++++++++++++++++++---\n t/t1512-rev-parse-disambiguation.sh |  84 +++++++++++++++++++++\n 2 files changed, 184 insertions(+), 12 deletions(-)\n\nRange-diff against v5:\n1:  767165d096d ! 1:  27f267ad555 object-name tests: add tests for ambiguous object blind spots\n    @@ Commit message\n         prefix are the same for both SHA-1 and SHA-256. This uses strings that\n         I mined, and have the same prefix when hashed with both.\n     \n    +    We \"test_cmp\" the full output to guard against any future regressions,\n    +    and because a subsequent commit will tweak it. Showing a diff of how\n    +    the output changes is helpful to explain those subsequent commits.\n    +\n         1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\t\techo 1bbfctrkc | git hash-object -w --stdin\n     +\t) &&\n     +\n    ++\ttest_must_fail git -C blob.prefix rev-parse dead &&\n     +\tcat >expect <<-\\EOF &&\n     +\terror: short object ID beef... is ambiguous\n     +\thint: The candidates are:\n2:  ee86912f1c1 ! 2:  c78243dc701 object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n    @@ Commit message\n         added in the preceding commit.\n     \n         So saying \"unknown type\" here was always misleading, we really meant\n    -    to say that we had a failure parsing the object at all, if the problem\n    -    is only that it's type is unknown we won't reach this code.\n    +    to say that we had a failure parsing the object at all, i.e. that we\n    +    had repository corruption. If the problem is only that it's type is\n    +    unknown we won't reach this code.\n     \n         So let's emit a generic \"[bad object]\" instead. As our tests added in\n         the preceding commit show, we'll have emitted various \"error\" output\n3:  b79964483e8 ! 3:  daebc95542c object-name: make ambiguous object output translatable\n    @@ Commit message\n            then SHA-1, 2018-05-10)\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n      ## object-name.c ##\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     -\tstrbuf_addstr(&desc, type_name(type));\n      \n      \tif (type == OBJ_COMMIT) {\n    -+\t\tstruct strbuf ad = STRBUF_INIT;\n    -+\t\tstruct strbuf s = STRBUF_INIT;\n    ++\t\tstruct strbuf date = STRBUF_INIT;\n    ++\t\tstruct strbuf msg = STRBUF_INIT;\n      \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n     +\n      \t\tif (commit) {\n      \t\t\tstruct pretty_print_context pp = {0};\n      \t\t\tpp.date_mode.type = DATE_SHORT;\n     -\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n    -+\t\t\tformat_commit_message(commit, \"%ad\", &ad, &pp);\n    -+\t\t\tformat_commit_message(commit, \"%s\", &s, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n    ++\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n      \t\t}\n     +\n     +\t\t/*\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\t *\n     +\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n     +\t\t */\n    -+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n    ++\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n    ++\t\t\t    hash, date.buf, msg.buf);\n     +\n    -+\t\tstrbuf_release(&ad);\n    -+\t\tstrbuf_release(&s);\n    ++\t\tstrbuf_release(&date);\n    ++\t\tstrbuf_release(&msg);\n      \t} else if (type == OBJ_TAG) {\n      \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n     +\t\tconst char *tag_tag = \"\";\n4:  36b6b440c37 = 4:  b5aa6e266f6 object-name: show date for ambiguous tag objects\n5:  8880c283559 = 5:  644b076b2a6 object-name: iterate ambiguous objects before showing header\n6:  78bb0995f08 ! 6:  6a31cfcfc29 object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \t\t *\n      \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n      \t\t */\n    --\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n    -+\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, ad.buf, s.buf);\n    +-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n    +-\t\t\t    hash, date.buf, msg.buf);\n    ++\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, date.buf,\n    ++\t\t\t    msg.buf);\n      \n    - \t\tstrbuf_release(&ad);\n    - \t\tstrbuf_release(&s);\n    + \t\tstrbuf_release(&date);\n    + \t\tstrbuf_release(&msg);\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n      \t\t * object.c, it should (hopefully) already be\n      \t\t * translated.\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445080","messageId":"patch-v6-1.6-27f267ad555-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:34:57Z","receivedAt":"2021-12-28T14:35:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the tests for ambiguous objects to check how we handle objects\nwhere we return OBJ_BAD when trying to parse them. As noted in [1] we\nhave a blindspot when it comes to this behavior.\n\nSince we need to add new test data here let's extend these tests to be\ntested under SHA-256, in d7a2fc82491 (t1512: skip test if not using\nSHA-1, 2018-05-13) all of the existing tests were skipped, as they\nrely on specific SHA-1 object IDs.\n\nFor these tests it only matters that the first 4 characters of the OID\nprefix are the same for both SHA-1 and SHA-256. This uses strings that\nI mined, and have the same prefix when hashed with both.\n\nWe \"test_cmp\" the full output to guard against any future regressions,\nand because a subsequent commit will tweak it. Showing a diff of how\nthe output changes is helpful to explain those subsequent commits.\n\n1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1512-rev-parse-disambiguation.sh | 85 +++++++++++++++++++++++++++++\n 1 file changed, 85 insertions(+)\n\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 7891a6becf3..60d2a457067 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -25,6 +25,91 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+test_cmp_failed_rev_parse () {\n+\tdir=$1\n+\trev=$2\n+\tshift\n+\n+\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n+\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../g\" <actual.raw >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ambiguous blob output' '\n+\tgit init --bare blob.prefix &&\n+\t(\n+\t\tcd blob.prefix &&\n+\n+\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n+\t\techo brocdnra | git hash-object -w --stdin &&\n+\t\techo brigddsv | git hash-object -w --stdin &&\n+\n+\t\t# Both start with \"beef..\"\n+\t\techo 1agllotbh | git hash-object -w --stdin &&\n+\t\techo 1bbfctrkc | git hash-object -w --stdin\n+\t) &&\n+\n+\ttest_must_fail git -C blob.prefix rev-parse dead &&\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID beef... is ambiguous\n+\thint: The candidates are:\n+\thint:   beef... blob\n+\thint:   beef... blob\n+\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.prefix beef\n+'\n+\n+test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n+\tgit init --bare blob.bad &&\n+\t(\n+\t\tcd blob.bad &&\n+\n+\t\t# Both have the prefix \"bad0\"\n+\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n+\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n+\t) &&\n+\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID bad0... is ambiguous\n+\thint: The candidates are:\n+\tfatal: invalid object type\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.bad bad0\n+'\n+\n+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n+\tgit init --bare blob.corrupt &&\n+\t(\n+\t\tcd blob.corrupt &&\n+\n+\t\t# Both have the prefix \"cafe\"\n+\t\techo bnkxmdwz | git hash-object -w --stdin &&\n+\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n+\n+\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n+\t\tchmod 755 $oidf &&\n+\t\techo broken >$oidf\n+\t) &&\n+\n+\tcat >expect <<-\\EOF &&\n+\terror: short object ID cafe... is ambiguous\n+\thint: The candidates are:\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\thint:   cafe... unknown type\n+\thint:   cafe... blob\n+\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+\ttest_cmp_failed_rev_parse blob.corrupt cafe\n+'\n+\n if ! test_have_prereq SHA1\n then\n \tskip_all='not using SHA-1 for objects'\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445081","messageId":"patch-v6-3.6-daebc95542c-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 3/6] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:34:59Z","receivedAt":"2021-12-28T14:35:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators.\n\nBy being able to customize the \"<SP><SP>%s\\n\" format we're even ready\nfor RTL languages, who'd presumably like to change that to\n\"%s<SP><SP>\\n\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n object-name.c | 65 +++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 58 insertions(+), 7 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 9750634ee76..dcf3ab99990 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,38 +356,89 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n \n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \n \tif (type < 0) {\n-\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous object\n+\t\t * output shown when we cannot look up or parse the\n+\t\t * object in question. E.g. \"deadbeef [bad object]\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n-\tstrbuf_addstr(&desc, type_name(type));\n \n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tstruct strbuf msg = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n+\t\t\t    hash, date.buf, msg.buf);\n+\n+\t\tstrbuf_release(&date);\n+\t\tstrbuf_release(&msg);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n+\t\tconst char *tag_tag = \"\";\n+\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_tag = tag->tag;\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c, it should (hopefully) already be\n+\t\t * translated.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n \t}\n \n+\n out:\n-\tadvise(\"  %s %s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output\n+\t * from describe_ambiguous_object() above.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445082","messageId":"patch-v6-2.6-c78243dc701-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 2/6] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:34:58Z","receivedAt":"2021-12-28T14:35:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the \"unknown type\" handling in the code that displays the\nambiguous object list to assert() that we're either going to get the\n\"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\nreturn value from oid_object_info().\n\nSee [1] for the current output, and [1] for the commit that added the\n\"unknown type\" handling.\n\nWe are never going to get an \"unknown type\" in the sense of custom\ntypes crafted with \"hash-object --literally\", since we're not using\nthe OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n\nIf we manage to otherwise unpack such an object without errors we'll\ndie() in parse_loose_header_extended() called by sort_ambiguous()\nbefore we get to show_ambiguous_object(), as is asserted by the test\nadded in the preceding commit.\n\nSo saying \"unknown type\" here was always misleading, we really meant\nto say that we had a failure parsing the object at all, i.e. that we\nhad repository corruption. If the problem is only that it's type is\nunknown we won't reach this code.\n\nSo let's emit a generic \"[bad object]\" instead. As our tests added in\nthe preceding commit show, we'll have emitted various \"error\" output\nalready in those cases.\n\nWe should do better in the truly \"unknown type\" cases, which we'd need\nto handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\nflag. But let's leave that for some future improvement. In a\nsubsequent commit I'll improve the output we do show, and not having\nto handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\nsimplifies that change.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 14 ++++++++++++--\n t/t1512-rev-parse-disambiguation.sh |  2 +-\n 2 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..9750634ee76 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,16 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\n+\tif (type < 0) {\n+\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\tgoto out;\n+\t}\n+\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n+\tstrbuf_addstr(&desc, type_name(type));\n+\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -374,9 +384,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n \t}\n \n-\tadvise(\"  %s %s%s\",\n+out:\n+\tadvise(\"  %s %s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n \t       desc.buf);\n \n \tstrbuf_release(&desc);\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 60d2a457067..d68c411bfc7 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -101,7 +101,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n-\thint:   cafe... unknown type\n+\thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n \tUse '\\''--'\\'' to separate paths from revisions, like this:\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445083","messageId":"patch-v6-4.6-b5aa6e266f6-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 4/6] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:35:00Z","receivedAt":"2021-12-28T14:35:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line of:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex dcf3ab99990..990f384129e 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -403,21 +403,26 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n+\t\ttimestamp_t tag_date = 0;\n \n-\t\tif (!parse_tag(tag) && tag->tag)\n+\t\tif (!parse_tag(tag) && tag->tag) {\n \t\t\ttag_tag = tag->tag;\n+\t\t\ttag_date = tag->date;\n+\t\t}\n \n \t\t/*\n \t\t * TRANSLATORS: This is a line of\n \t\t * ambiguous tag object output. E.g.:\n \t\t *\n-\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n \t\t *\n \t\t * The second argument is the \"tag\" string from\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n+\t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445084","messageId":"patch-v6-5.6-644b076b2a6-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 5/6] object-name: iterate ambiguous objects before showing header","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:35:01Z","receivedAt":"2021-12-28T14:35:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"The candidates are\" header that's shown for ambiguous\nobjects to be shown after we've iterated over all of the objects.\n\nIf we get any errors while doing so we don't want to split up the the\nheader and the list as a result. The two will now be printed together,\nas shown in the updated testcase.\n\nAs we're accumulating the lines into as \"struct strbuf\" before\nemitting them we need to add a trailing newline to the call in\nshow_ambiguous_object(). This and the change from \"The candidates\nare:\" to \"The candidates are:\\n%s\" helps to give translators more\ncontext.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 27 +++++++++++++++++++++++----\n t/t1512-rev-parse-disambiguation.sh |  3 +--\n 2 files changed, 24 insertions(+), 6 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 990f384129e..743d272800d 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -351,9 +351,16 @@ static int init_object_disambiguation(struct repository *r,\n \treturn 0;\n }\n \n+struct ambiguous_output {\n+\tconst struct disambiguate_state *ds;\n+\tstruct strbuf advice;\n+};\n+\n static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n-\tconst struct disambiguate_state *ds = data;\n+\tstruct ambiguous_output *state = data;\n+\tconst struct disambiguate_state *ds = state->ds;\n+\tstruct strbuf *advice = &state->advice;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n \tconst char *hash;\n@@ -443,7 +450,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * TRANSLATORS: This is line item of ambiguous object output\n \t * from describe_ambiguous_object() above.\n \t */\n-\tadvise(_(\"  %s\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n@@ -542,6 +549,10 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n+\t\tstruct ambiguous_output out = {\n+\t\t\t.ds = &ds,\n+\t\t\t.advice = STRBUF_INIT,\n+\t\t};\n \n \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n \n@@ -554,13 +565,21 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tif (!ds.ambiguous)\n \t\t\tds.fn = NULL;\n \n-\t\tadvise(_(\"The candidates are:\"));\n \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n \t\tsort_ambiguous_oid_array(r, &collect);\n \n-\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &out))\n \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n+\n+\t\t/*\n+\t\t * TRANSLATORS: The argument is the list of ambiguous\n+\t\t * objects composed in show_ambiguous_object(). See\n+\t\t * its \"TRANSLATORS\" comments for details.\n+\t\t */\n+\t\tadvise(_(\"The candidates are:\\n%s\"), out.advice.buf);\n+\n \t\toid_array_clear(&collect);\n+\t\tstrbuf_release(&out.advice);\n \t}\n \n \treturn status;\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex d68c411bfc7..cb8ee3d65ed 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -74,7 +74,6 @@ test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n \n \tcat >expect <<-\\EOF &&\n \terror: short object ID bad0... is ambiguous\n-\thint: The candidates are:\n \tfatal: invalid object type\n \tEOF\n \ttest_cmp_failed_rev_parse blob.bad bad0\n@@ -96,11 +95,11 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \n \tcat >expect <<-\\EOF &&\n \terror: short object ID cafe... is ambiguous\n-\thint: The candidates are:\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n+\thint: The candidates are:\n \thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445085","messageId":"patch-v6-6.6-6a31cfcfc29-20211228T143223Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v6 6/6] object-name: re-use \"struct strbuf\" in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T14:35:02Z","receivedAt":"2021-12-28T14:35:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reduce the allocations done by show_ambiguous_object() by moving the\n\"desc\" strbuf into the \"struct ambiguous_output\" introduced in the\npreceding commit.\n\nThis doesn't matter for optimization purposes, but since we're\naccumulating a \"struct strbuf advice\" anyway let's follow that pattern\nand add a \"struct strbuf sb\", we can then strbuf_reset() it rather\nthan calling strbuf_release() for each call to\nshow_ambiguous_object().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 21 ++++++++++++---------\n 1 file changed, 12 insertions(+), 9 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 743d272800d..2d60e5177d3 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -354,6 +354,7 @@ static int init_object_disambiguation(struct repository *r,\n struct ambiguous_output {\n \tconst struct disambiguate_state *ds;\n \tstruct strbuf advice;\n+\tstruct strbuf sb;\n };\n \n static int show_ambiguous_object(const struct object_id *oid, void *data)\n@@ -361,7 +362,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tstruct ambiguous_output *state = data;\n \tconst struct disambiguate_state *ds = state->ds;\n \tstruct strbuf *advice = &state->advice;\n-\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct strbuf *sb = &state->sb;\n \tint type;\n \tconst char *hash;\n \n@@ -377,7 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * output shown when we cannot look up or parse the\n \t\t * object in question. E.g. \"deadbeef [bad object]\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n@@ -402,8 +403,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t *\n \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n-\t\t\t    hash, date.buf, msg.buf);\n+\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, date.buf,\n+\t\t\t    msg.buf);\n \n \t\tstrbuf_release(&date);\n \t\tstrbuf_release(&msg);\n@@ -427,7 +428,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * object.c, it should (hopefully) already be\n \t\t * translated.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n \t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n \t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n@@ -435,13 +436,13 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef tree\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s tree\"), hash);\n \t} else if (type == OBJ_BLOB) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef blob\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s blob\"), hash);\n \t}\n \n \n@@ -450,9 +451,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * TRANSLATORS: This is line item of ambiguous object output\n \t * from describe_ambiguous_object() above.\n \t */\n-\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), sb->buf);\n \n-\tstrbuf_release(&desc);\n+\tstrbuf_reset(sb);\n \treturn 0;\n }\n \n@@ -551,6 +552,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n \t\tstruct ambiguous_output out = {\n \t\t\t.ds = &ds,\n+\t\t\t.sb = STRBUF_INIT,\n \t\t\t.advice = STRBUF_INIT,\n \t\t};\n \n@@ -580,6 +582,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \t\toid_array_clear(&collect);\n \t\tstrbuf_release(&out.advice);\n+\t\tstrbuf_release(&out.sb);\n \t}\n \n \treturn status;\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445089","messageId":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v8 0/7] progress: test fixes / cleanup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:18:56Z","receivedAt":"2021-12-28T15:19:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Various test, leak and other fixes for the progress.c code and its\ntests. This v8 addresses feedback on v7[1] by Johannes\nAltmanninger. For that round I accidentally broke the In-Reply-To\nchain, so I'm replying to the v6 here to attach it to the original\nthread again.\n\n1. https://lore.kernel.org/git/cover-v7-0.7-00000000000-20211217T041945Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (7):\n  leak tests: fix a memory leak in \"test-progress\" helper\n  progress.c test helper: add missing braces\n  progress.c tests: make start/stop commands on stdin\n  progress.c tests: test some invalid usage\n  progress.c: add temporary variable from progress struct\n  pack-bitmap-write.c: don't return without stop_progress()\n  *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)\n\n builtin/bisect--helper.c    |  2 +-\n builtin/bundle.c            |  2 +-\n compat/mingw.c              |  2 +-\n pack-bitmap-write.c         |  6 +--\n progress.c                  | 14 +++---\n t/helper/test-progress.c    | 52 +++++++++++++++-----\n t/t0500-progress-display.sh | 94 ++++++++++++++++++++++++++++---------\n 7 files changed, 126 insertions(+), 46 deletions(-)\n\nRange-diff against v7:\n1:  5367293ee84 ! 1:  aa08dab654d leak tests: fix a memory leaks in \"test-progress\" helper\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    leak tests: fix a memory leaks in \"test-progress\" helper\n    +    leak tests: fix a memory leak in \"test-progress\" helper\n     \n         Fix a memory leak in the test-progress helper, and mark the\n         corresponding \"t0500-progress-display.sh\" test as being leak-free\n2:  81788101763 = 2:  3ecdab074b6 progress.c test helper: add missing braces\n3:  d685c248686 ! 3:  271f6d7ec3b progress.c tests: make start/stop commands on stdin\n    @@ t/helper/test-progress.c\n      #include \"progress.h\"\n      #include \"strbuf.h\"\n     +#include \"string-list.h\"\n    -+\n    -+/*\n    -+ * We can't use \"end + 1\" as an argument to start_progress() below, it\n    -+ * doesn't xstrdup() its \"title\" argument. We need to hold onto a\n    -+ * valid \"char *\" for it until the end.\n    -+ */\n    -+static char *dup_title(struct string_list *titles, const char *title)\n    -+{\n    -+\treturn string_list_insert(titles, title)->string;\n    -+}\n      \n      int cmd__progress(int argc, const char **argv)\n      {\n    @@ t/helper/test-progress.c\n     -\t\tif (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n     +\t\tif (skip_prefix(line.buf, \"start \", (const char **) &end)) {\n     +\t\t\tuint64_t total = strtoull(end, &end, 10);\n    -+\t\t\tif (*end == '\\0')\n    -+\t\t\t\tprogress = start_progress(default_title, total);\n    ++\t\t\tconst char *title;\n    ++\t\t\tconst char *str;\n    ++\n    ++\t\t\t/*\n    ++\t\t\t * We can't use \"end + 1\" as an argument to\n    ++\t\t\t * start_progress(), it doesn't xstrdup() its\n    ++\t\t\t * \"title\" argument. We need to hold onto a\n    ++\t\t\t * valid \"char *\" for it until the end.\n    ++\t\t\t */\n    ++\t\t\tif (!*end)\n    ++\t\t\t\ttitle = default_title;\n     +\t\t\telse if (*end == ' ')\n    -+\t\t\t\tprogress = start_progress(dup_title(&titles,\n    -+\t\t\t\t\t\t\t\t    end + 1),\n    -+\t\t\t\t\t\t\t  total);\n    ++\t\t\t\ttitle = string_list_insert(&titles, end + 1)->string;\n     +\t\t\telse\n     +\t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n    ++\n    ++\t\t\tstr = title ? title : default_title;\n    ++\t\t\tprogress = start_progress(str, total);\n     +\t\t} else if (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n      \t\t\tuint64_t item_count = strtoull(end, &end, 10);\n      \t\t\tif (*end != '\\0')\n4:  40e446da277 = 4:  7c1b8b287c5 progress.c tests: test some invalid usage\n5:  c2303bfd130 ! 5:  72a31bd7191 progress.c: add temporary variable from progress struct\n    @@ Metadata\n      ## Commit message ##\n         progress.c: add temporary variable from progress struct\n     \n    -    Add a temporary \"progress\" variable for the dereferenced p_progress\n    -    pointer to a \"struct progress *\". Before 98a13647408 (trace2: log\n    -    progress time and throughput, 2020-05-12) we didn't dereference\n    -    \"p_progress\" in this function, now that we do it's easier to read the\n    -    code if we work with a \"progress\" struct pointer like everywhere else,\n    -    instead of a pointer to a pointer.\n    +    Since 98a13647408 (trace2: log progress time and throughput,\n    +    2020-05-12) stop_progress() dereferences a \"struct progress **\"\n    +    parameter in several places. Extract a dereferenced variable (like in\n    +    stop_progress_msg()) to reduce clutter and make it clearer who needs\n    +    to write to this parameter.\n    +\n    +    Now instead of using \"*p_progress\" several times in stop_progress() we\n    +    check it once for NULL and then use a dereferenced \"progress\" variable\n    +    thereafter. This continues the same pattern used in the above\n    +    stop_progress() function, see ac900fddb7f (progress: don't dereference\n    +    before checking for NULL, 2020-08-10).\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## progress.c ##\n    -@@ progress.c: void stop_progress(struct progress **p_progress)\n    - \tfinish_if_sparse(*p_progress);\n    +@@ progress.c: static void finish_if_sparse(struct progress *progress)\n    + \n    + void stop_progress(struct progress **p_progress)\n    + {\n    ++\tstruct progress *progress;\n    + \tif (!p_progress)\n    + \t\tBUG(\"don't provide NULL to stop_progress\");\n    ++\tprogress = *p_progress;\n    + \n    +-\tfinish_if_sparse(*p_progress);\n    ++\tfinish_if_sparse(progress);\n      \n    - \tif (*p_progress) {\n    -+\t\tstruct progress *progress = *p_progress;\n    +-\tif (*p_progress) {\n    ++\tif (progress) {\n      \t\ttrace2_data_intmax(\"progress\", the_repository, \"total_objects\",\n    - \t\t\t\t   (*p_progress)->total);\n    +-\t\t\t\t   (*p_progress)->total);\n    ++\t\t\t\t   progress->total);\n      \n    - \t\tif ((*p_progress)->throughput)\n    +-\t\tif ((*p_progress)->throughput)\n    ++\t\tif (progress->throughput)\n      \t\t\ttrace2_data_intmax(\"progress\", the_repository,\n      \t\t\t\t\t   \"total_bytes\",\n     -\t\t\t\t\t   (*p_progress)->throughput->curr_total);\n6:  776362de897 ! 6:  0bd08e1b018 pack-bitmap-write.c: don't return without stop_progress()\n    @@ Commit message\n         reached the early exit in this function.\n     \n         We could call stop_progress() before we return, but better yet is to\n    -    defer calling start_progress() until we need it.\n    -\n    -    This will matter in a subsequent commit where we BUG(...) out if this\n    -    happens, and matters now e.g. because we don't have a corresponding\n    -    \"region_end\" for the progress trace2 event.\n    +    defer calling start_progress() until we need it. For now this only\n    +    matters in practice because we'd previously omit the \"region_leave\"\n    +    for the progress trace2 event.\n     \n         Suggested-by: SZEDER Gábor <szeder.dev@gmail.com>\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n7:  0670d1aa5f2 ! 7:  060483fb5ce various *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    various *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)\n    +    *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)\n     \n         We have over 50 uses of \"isatty(1)\" and \"isatty(2)\" in the codebase,\n    -    and around 10 \"isatty(0)\", but these used the\n    +    and around 10 \"isatty(0)\", but three callers used the\n         {STDIN_FILENO,STD{OUT,ERR}_FILENO} macros in \"stdlib.h\" to refer to\n         them.\n     \n    -    Let's change these for consistency, and because another commit that\n    -    would like to be based on top of this one[1] has a recipe to change\n    -    all of these for ad-hoc testing, not needing to match these with that\n    -    ad-hoc regex will make things easier to explain. Only one of these is\n    -    related to the \"struct progress\" code which it discusses, but let's\n    -    change all of these while we're at it.\n    +    Let's change these for consistency.  This makes it easier to change\n    +    all calls to isatty() at a whim, which is useful to test some\n    +    scenarios[1].\n     \n         1. https://lore.kernel.org/git/patch-v6-8.8-bff919994b5-20211102T122507Z-avarab@gmail.com/\n     \n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445090","messageId":"patch-v8-1.7-aa08dab654d-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 1/7] leak tests: fix a memory leak in \"test-progress\" helper","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:18:57Z","receivedAt":"2021-12-28T15:19:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak in the test-progress helper, and mark the\ncorresponding \"t0500-progress-display.sh\" test as being leak-free\nunder SANITIZE=leak. This fixes a leak added in 2bb74b53a4 (Test the\nprogress display, 2019-09-16).\n\nMy 48f68715b14 (tr2: stop leaking \"thread_name\" memory, 2021-08-27)\nhad fixed another memory leak in this test (as it did some trace2\ntesting).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-progress.c    | 1 +\n t/t0500-progress-display.sh | 1 +\n 2 files changed, 2 insertions(+)\n\ndiff --git a/t/helper/test-progress.c b/t/helper/test-progress.c\nindex 5d05cbe7894..9265e6ab7cf 100644\n--- a/t/helper/test-progress.c\n+++ b/t/helper/test-progress.c\n@@ -69,6 +69,7 @@ int cmd__progress(int argc, const char **argv)\n \t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n \t}\n \tstop_progress(&progress);\n+\tstrbuf_release(&line);\n \n \treturn 0;\n }\ndiff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh\nindex 22058b503ac..f37cf2eb9c9 100755\n--- a/t/t0500-progress-display.sh\n+++ b/t/t0500-progress-display.sh\n@@ -2,6 +2,7 @@\n \n test_description='progress display'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n show_cr () {\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445091","messageId":"patch-v8-2.7-3ecdab074b6-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 2/7] progress.c test helper: add missing braces","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:18:58Z","receivedAt":"2021-12-28T15:19:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If we have braces on one arm of an if/else all of them should have it,\nper the CodingGuidelines's \"When there are multiple arms to a\nconditional[...]\" advice. This formatting change makes a subsequent\ncommit smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-progress.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-progress.c b/t/helper/test-progress.c\nindex 9265e6ab7cf..50fd3be3dad 100644\n--- a/t/helper/test-progress.c\n+++ b/t/helper/test-progress.c\n@@ -63,10 +63,11 @@ int cmd__progress(int argc, const char **argv)\n \t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n \t\t\tprogress_test_ns = test_ms * 1000 * 1000;\n \t\t\tdisplay_throughput(progress, byte_count);\n-\t\t} else if (!strcmp(line.buf, \"update\"))\n+\t\t} else if (!strcmp(line.buf, \"update\")) {\n \t\t\tprogress_test_force_update();\n-\t\telse\n+\t\t} else {\n \t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n+\t\t}\n \t}\n \tstop_progress(&progress);\n \tstrbuf_release(&line);\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445092","messageId":"patch-v8-3.7-271f6d7ec3b-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 3/7] progress.c tests: make start/stop commands on stdin","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:18:59Z","receivedAt":"2021-12-28T15:19:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the usage of the \"test-tool progress\" introduced in\n2bb74b53a49 (Test the progress display, 2019-09-16) to take command\nlike \"start\" and \"stop\" on stdin, instead of running them implicitly.\n\nThis makes for tests that are easier to read, since the recipe will\nmirror the API usage, and allows for easily testing invalid usage that\nwould yield (or should yield) a BUG(), e.g. providing two \"start\"\ncalls in a row. A subsequent commit will add such tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-progress.c    | 46 ++++++++++++++++++++++-------\n t/t0500-progress-display.sh | 58 +++++++++++++++++++++++--------------\n 2 files changed, 72 insertions(+), 32 deletions(-)\n\ndiff --git a/t/helper/test-progress.c b/t/helper/test-progress.c\nindex 50fd3be3dad..becc163375f 100644\n--- a/t/helper/test-progress.c\n+++ b/t/helper/test-progress.c\n@@ -3,6 +3,9 @@\n  *\n  * Reads instructions from standard input, one instruction per line:\n  *\n+ *   \"start <total>[ <title>]\" - Call start_progress(title, total),\n+ *                               Uses the default title of \"Working hard\"\n+ *                               if the \" <title>\" is omitted.\n  *   \"progress <items>\" - Call display_progress() with the given item count\n  *                        as parameter.\n  *   \"throughput <bytes> <millis> - Call display_throughput() with the given\n@@ -10,6 +13,7 @@\n  *                                  specify the time elapsed since the\n  *                                  start_progress() call.\n  *   \"update\" - Set the 'progress_update' flag.\n+ *   \"stop\" - Call stop_progress().\n  *\n  * See 't0500-progress-display.sh' for examples.\n  */\n@@ -19,34 +23,52 @@\n #include \"parse-options.h\"\n #include \"progress.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n int cmd__progress(int argc, const char **argv)\n {\n-\tint total = 0;\n-\tconst char *title;\n+\tconst char *const default_title = \"Working hard\";\n+\tstruct string_list titles = STRING_LIST_INIT_DUP;\n \tstruct strbuf line = STRBUF_INIT;\n-\tstruct progress *progress;\n+\tstruct progress *progress = NULL;\n \n \tconst char *usage[] = {\n-\t\t\"test-tool progress [--total=<n>] <progress-title>\",\n+\t\t\"test-tool progress <stdin\",\n \t\tNULL\n \t};\n \tstruct option options[] = {\n-\t\tOPT_INTEGER(0, \"total\", &total, \"total number of items\"),\n \t\tOPT_END(),\n \t};\n \n \targc = parse_options(argc, argv, NULL, options, usage, 0);\n-\tif (argc != 1)\n-\t\tdie(\"need a title for the progress output\");\n-\ttitle = argv[0];\n+\tif (argc)\n+\t\tusage_with_options(usage, options);\n \n \tprogress_testing = 1;\n-\tprogress = start_progress(title, total);\n \twhile (strbuf_getline(&line, stdin) != EOF) {\n \t\tchar *end;\n \n-\t\tif (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n+\t\tif (skip_prefix(line.buf, \"start \", (const char **) &end)) {\n+\t\t\tuint64_t total = strtoull(end, &end, 10);\n+\t\t\tconst char *title;\n+\t\t\tconst char *str;\n+\n+\t\t\t/*\n+\t\t\t * We can't use \"end + 1\" as an argument to\n+\t\t\t * start_progress(), it doesn't xstrdup() its\n+\t\t\t * \"title\" argument. We need to hold onto a\n+\t\t\t * valid \"char *\" for it until the end.\n+\t\t\t */\n+\t\t\tif (!*end)\n+\t\t\t\ttitle = default_title;\n+\t\t\telse if (*end == ' ')\n+\t\t\t\ttitle = string_list_insert(&titles, end + 1)->string;\n+\t\t\telse\n+\t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n+\n+\t\t\tstr = title ? title : default_title;\n+\t\t\tprogress = start_progress(str, total);\n+\t\t} else if (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n \t\t\tuint64_t item_count = strtoull(end, &end, 10);\n \t\t\tif (*end != '\\0')\n \t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n@@ -65,12 +87,14 @@ int cmd__progress(int argc, const char **argv)\n \t\t\tdisplay_throughput(progress, byte_count);\n \t\t} else if (!strcmp(line.buf, \"update\")) {\n \t\t\tprogress_test_force_update();\n+\t\t} else if (!strcmp(line.buf, \"stop\")) {\n+\t\t\tstop_progress(&progress);\n \t\t} else {\n \t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n \t\t}\n \t}\n-\tstop_progress(&progress);\n \tstrbuf_release(&line);\n+\tstring_list_clear(&titles, 0);\n \n \treturn 0;\n }\ndiff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh\nindex f37cf2eb9c9..27ab4218b01 100755\n--- a/t/t0500-progress-display.sh\n+++ b/t/t0500-progress-display.sh\n@@ -18,6 +18,7 @@ test_expect_success 'simple progress display' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 0\n \tupdate\n \tprogress 1\n \tupdate\n@@ -26,8 +27,9 @@ test_expect_success 'simple progress display' '\n \tprogress 4\n \tupdate\n \tprogress 5\n+\tstop\n \tEOF\n-\ttest-tool progress \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -42,11 +44,13 @@ test_expect_success 'progress display with total' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 3\n \tprogress 1\n \tprogress 2\n \tprogress 3\n+\tstop\n \tEOF\n-\ttest-tool progress --total=3 \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -63,14 +67,14 @@ Working hard.......2.........3.........4.........5.........6:\n EOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 100000 Working hard.......2.........3.........4.........5.........6\n \tprogress 100\n \tprogress 1000\n \tprogress 10000\n \tprogress 100000\n+\tstop\n \tEOF\n-\ttest-tool progress --total=100000 \\\n-\t\t\"Working hard.......2.........3.........4.........5.........6\" \\\n-\t\t<in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -89,16 +93,16 @@ Working hard.......2.........3.........4.........5.........6:\n EOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 100000 Working hard.......2.........3.........4.........5.........6\n \tupdate\n \tprogress 1\n \tupdate\n \tprogress 2\n \tprogress 10000\n \tprogress 100000\n+\tstop\n \tEOF\n-\ttest-tool progress --total=100000 \\\n-\t\t\"Working hard.......2.........3.........4.........5.........6\" \\\n-\t\t<in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -117,14 +121,14 @@ Working hard.......2.........3.........4.........5.........6:\n EOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 100000 Working hard.......2.........3.........4.........5.........6\n \tprogress 25000\n \tprogress 50000\n \tprogress 75000\n \tprogress 100000\n+\tstop\n \tEOF\n-\ttest-tool progress --total=100000 \\\n-\t\t\"Working hard.......2.........3.........4.........5.........6\" \\\n-\t\t<in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -141,14 +145,14 @@ Working hard.......2.........3.........4.........5.........6.........7.........:\n EOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 100000 Working hard.......2.........3.........4.........5.........6.........7.........\n \tprogress 25000\n \tprogress 50000\n \tprogress 75000\n \tprogress 100000\n+\tstop\n \tEOF\n-\ttest-tool progress --total=100000 \\\n-\t\t\"Working hard.......2.........3.........4.........5.........6.........7.........\" \\\n-\t\t<in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -165,12 +169,14 @@ test_expect_success 'progress shortens - crazy caller' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 1000\n \tprogress 100\n \tprogress 200\n \tprogress 1\n \tprogress 1000\n+\tstop\n \tEOF\n-\ttest-tool progress --total=1000 \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -186,6 +192,7 @@ test_expect_success 'progress display with throughput' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 0\n \tthroughput 102400 1000\n \tupdate\n \tprogress 10\n@@ -198,8 +205,9 @@ test_expect_success 'progress display with throughput' '\n \tthroughput 409600 4000\n \tupdate\n \tprogress 40\n+\tstop\n \tEOF\n-\ttest-tool progress \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -215,6 +223,7 @@ test_expect_success 'progress display with throughput and total' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 40\n \tthroughput 102400 1000\n \tprogress 10\n \tthroughput 204800 2000\n@@ -223,8 +232,9 @@ test_expect_success 'progress display with throughput and total' '\n \tprogress 30\n \tthroughput 409600 4000\n \tprogress 40\n+\tstop\n \tEOF\n-\ttest-tool progress --total=40 \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -240,6 +250,7 @@ test_expect_success 'cover up after throughput shortens' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 0\n \tthroughput 409600 1000\n \tupdate\n \tprogress 1\n@@ -252,8 +263,9 @@ test_expect_success 'cover up after throughput shortens' '\n \tthroughput 1638400 4000\n \tupdate\n \tprogress 4\n+\tstop\n \tEOF\n-\ttest-tool progress \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -268,6 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' '\n \tEOF\n \n \tcat >in <<-\\EOF &&\n+\tstart 0\n \tthroughput 1 1000\n \tupdate\n \tprogress 1\n@@ -277,8 +290,9 @@ test_expect_success 'cover up after throughput shortens a lot' '\n \tthroughput 3145728 3000\n \tupdate\n \tprogress 3\n+\tstop\n \tEOF\n-\ttest-tool progress \"Working hard\" <in 2>stderr &&\n+\ttest-tool progress <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n \ttest_cmp expect out\n@@ -286,6 +300,7 @@ test_expect_success 'cover up after throughput shortens a lot' '\n \n test_expect_success 'progress generates traces' '\n \tcat >in <<-\\EOF &&\n+\tstart 40\n \tthroughput 102400 1000\n \tupdate\n \tprogress 10\n@@ -298,10 +313,11 @@ test_expect_success 'progress generates traces' '\n \tthroughput 409600 4000\n \tupdate\n \tprogress 40\n+\tstop\n \tEOF\n \n-\tGIT_TRACE2_EVENT=\"$(pwd)/trace.event\" test-tool progress --total=40 \\\n-\t\t\"Working hard\" <in 2>stderr &&\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace.event\" test-tool progress \\\n+\t\t<in 2>stderr &&\n \n \t# t0212/parse_events.perl intentionally omits regions and data.\n \ttest_region progress \"Working hard\" trace.event &&\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445093","messageId":"patch-v8-4.7-7c1b8b287c5-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 4/7] progress.c tests: test some invalid usage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:19:00Z","receivedAt":"2021-12-28T15:19:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Test what happens when we \"stop\" without a \"start\", omit the \"stop\"\nafter a \"start\", or try to start two concurrent progress bars. This\nextends the trace2 tests added in 98a13647408 (trace2: log progress\ntime and throughput, 2020-05-12).\n\nThese tests are not merely testing the helper, but invalid API usage\nthat can happen if the progress.c API is misused.\n\nThe \"without stop\" test will leak under SANITIZE=leak, since this\nbuggy use of the API will leak memory. But let's not skip it entirely,\nor use the \"!SANITIZE_LEAK\" prerequisite check as we'd do with tests\nthat we're skipping due to leaks we haven't fixed yet. Instead\nannotate the specific command that should skip leak checking with\ncustom $LSAN_OPTIONS[1].\n\n1. https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t0500-progress-display.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh\nindex 27ab4218b01..59e9f226ea4 100755\n--- a/t/t0500-progress-display.sh\n+++ b/t/t0500-progress-display.sh\n@@ -325,4 +325,39 @@ test_expect_success 'progress generates traces' '\n \tgrep \"\\\"key\\\":\\\"total_bytes\\\",\\\"value\\\":\\\"409600\\\"\" trace.event\n '\n \n+test_expect_success 'progress generates traces: stop / start' '\n+\tcat >in <<-\\EOF &&\n+\tstart 0\n+\tstop\n+\tEOF\n+\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace-startstop.event\" test-tool progress \\\n+\t\t<in 2>stderr &&\n+\ttest_region progress \"Working hard\" trace-startstop.event\n+'\n+\n+test_expect_success 'progress generates traces: start without stop' '\n+\tcat >in <<-\\EOF &&\n+\tstart 0\n+\tEOF\n+\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace-start.event\" \\\n+\tLSAN_OPTIONS=detect_leaks=0 \\\n+\ttest-tool progress \\\n+\t\t<in 2>stderr &&\n+\tgrep region_enter.*progress trace-start.event &&\n+\t! grep region_leave.*progress trace-start.event\n+'\n+\n+test_expect_success 'progress generates traces: stop without start' '\n+\tcat >in <<-\\EOF &&\n+\tstop\n+\tEOF\n+\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace-stop.event\" test-tool progress \\\n+\t\t<in 2>stderr &&\n+\t! grep region_enter.*progress trace-stop.event &&\n+\t! grep region_leave.*progress trace-stop.event\n+'\n+\n test_done\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445094","messageId":"patch-v8-6.7-0bd08e1b018-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 6/7] pack-bitmap-write.c: don't return without stop_progress()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:19:02Z","receivedAt":"2021-12-28T15:19:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a bug that's been here since 7cc8f971085 (pack-objects: implement\nbitmap writing, 2013-12-21), we did not call stop_progress() if we\nreached the early exit in this function.\n\nWe could call stop_progress() before we return, but better yet is to\ndefer calling start_progress() until we need it. For now this only\nmatters in practice because we'd previously omit the \"region_leave\"\nfor the progress trace2 event.\n\nSuggested-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n pack-bitmap-write.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 9c55c1531e1..cab3eaa2acd 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -575,15 +575,15 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,\n \n \tQSORT(indexed_commits, indexed_commits_nr, date_compare);\n \n-\tif (writer.show_progress)\n-\t\twriter.progress = start_progress(\"Selecting bitmap commits\", 0);\n-\n \tif (indexed_commits_nr < 100) {\n \t\tfor (i = 0; i < indexed_commits_nr; ++i)\n \t\t\tpush_bitmapped_commit(indexed_commits[i]);\n \t\treturn;\n \t}\n \n+\tif (writer.show_progress)\n+\t\twriter.progress = start_progress(\"Selecting bitmap commits\", 0);\n+\n \tfor (;;) {\n \t\tstruct commit *chosen = NULL;\n \n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445095","messageId":"patch-v8-5.7-72a31bd7191-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 5/7] progress.c: add temporary variable from progress struct","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:19:01Z","receivedAt":"2021-12-28T15:19:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 98a13647408 (trace2: log progress time and throughput,\n2020-05-12) stop_progress() dereferences a \"struct progress **\"\nparameter in several places. Extract a dereferenced variable (like in\nstop_progress_msg()) to reduce clutter and make it clearer who needs\nto write to this parameter.\n\nNow instead of using \"*p_progress\" several times in stop_progress() we\ncheck it once for NULL and then use a dereferenced \"progress\" variable\nthereafter. This continues the same pattern used in the above\nstop_progress() function, see ac900fddb7f (progress: don't dereference\nbefore checking for NULL, 2020-08-10).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n progress.c | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/progress.c b/progress.c\nindex 680c6a8bf93..688749648be 100644\n--- a/progress.c\n+++ b/progress.c\n@@ -319,21 +319,23 @@ static void finish_if_sparse(struct progress *progress)\n \n void stop_progress(struct progress **p_progress)\n {\n+\tstruct progress *progress;\n \tif (!p_progress)\n \t\tBUG(\"don't provide NULL to stop_progress\");\n+\tprogress = *p_progress;\n \n-\tfinish_if_sparse(*p_progress);\n+\tfinish_if_sparse(progress);\n \n-\tif (*p_progress) {\n+\tif (progress) {\n \t\ttrace2_data_intmax(\"progress\", the_repository, \"total_objects\",\n-\t\t\t\t   (*p_progress)->total);\n+\t\t\t\t   progress->total);\n \n-\t\tif ((*p_progress)->throughput)\n+\t\tif (progress->throughput)\n \t\t\ttrace2_data_intmax(\"progress\", the_repository,\n \t\t\t\t\t   \"total_bytes\",\n-\t\t\t\t\t   (*p_progress)->throughput->curr_total);\n+\t\t\t\t\t   progress->throughput->curr_total);\n \n-\t\ttrace2_region_leave(\"progress\", (*p_progress)->title, the_repository);\n+\t\ttrace2_region_leave(\"progress\", progress->title, the_repository);\n \t}\n \n \tstop_progress_msg(p_progress, _(\"done\"));\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445096","messageId":"patch-v8-7.7-060483fb5ce-20211228T150728Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"[PATCH v8 7/7] *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T15:19:03Z","receivedAt":"2021-12-28T15:19:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We have over 50 uses of \"isatty(1)\" and \"isatty(2)\" in the codebase,\nand around 10 \"isatty(0)\", but three callers used the\n{STDIN_FILENO,STD{OUT,ERR}_FILENO} macros in \"stdlib.h\" to refer to\nthem.\n\nLet's change these for consistency.  This makes it easier to change\nall calls to isatty() at a whim, which is useful to test some\nscenarios[1].\n\n1. https://lore.kernel.org/git/patch-v6-8.8-bff919994b5-20211102T122507Z-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/bisect--helper.c | 2 +-\n builtin/bundle.c         | 2 +-\n compat/mingw.c           | 2 +-\n 3 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28a2e6a5750..21360a4e70b 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -830,7 +830,7 @@ static int bisect_autostart(struct bisect_terms *terms)\n \tfprintf_ln(stderr, _(\"You need to start by \\\"git bisect \"\n \t\t\t  \"start\\\"\\n\"));\n \n-\tif (!isatty(STDIN_FILENO))\n+\tif (!isatty(0))\n \t\treturn -1;\n \n \t/*\ndiff --git a/builtin/bundle.c b/builtin/bundle.c\nindex 5a85d7cd0fe..df69c651753 100644\n--- a/builtin/bundle.c\n+++ b/builtin/bundle.c\n@@ -56,7 +56,7 @@ static int parse_options_cmd_bundle(int argc,\n \n static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {\n \tint all_progress_implied = 0;\n-\tint progress = isatty(STDERR_FILENO);\n+\tint progress = isatty(2);\n \tstruct strvec pack_opts;\n \tint version = -1;\n \tint ret;\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex e14f2d5f77c..7c55d0f0414 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -2376,7 +2376,7 @@ int mingw_raise(int sig)\n \tswitch (sig) {\n \tcase SIGALRM:\n \t\tif (timer_fn == SIG_DFL) {\n-\t\t\tif (isatty(STDERR_FILENO))\n+\t\t\tif (isatty(2))\n \t\t\t\tfputs(\"Alarm clock\\n\", stderr);\n \t\t\texit(128 + SIGALRM);\n \t\t} else if (timer_fn != SIG_IGN)\n-- \n2.34.1.1257.g2af47340c7b\n\n"},{"id":"445106","messageId":"862118f6-e5dd-8e0a-139f-37b5a1682797@web.de","threadId":"56641","inReplyTo":"patch-v8-5.7-72a31bd7191-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 5/7] progress.c: add temporary variable from progress struct","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2021-12-28T16:05:21Z","receivedAt":"2021-12-28T16:05:37Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 28.12.21 um 16:19 schrieb Ævar Arnfjörð Bjarmason:\n> Since 98a13647408 (trace2: log progress time and throughput,\n> 2020-05-12) stop_progress() dereferences a \"struct progress **\"\n> parameter in several places. Extract a dereferenced variable (like in\n> stop_progress_msg()) to reduce clutter and make it clearer who needs\n> to write to this parameter.\n>\n> Now instead of using \"*p_progress\" several times in stop_progress() we\n> check it once for NULL and then use a dereferenced \"progress\" variable\n> thereafter. This continues the same pattern used in the above\n> stop_progress() function, see ac900fddb7f (progress: don't dereference\n> before checking for NULL, 2020-08-10).\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  progress.c | 14 ++++++++------\n>  1 file changed, 8 insertions(+), 6 deletions(-)\n>\n> diff --git a/progress.c b/progress.c\n> index 680c6a8bf93..688749648be 100644\n> --- a/progress.c\n> +++ b/progress.c\n> @@ -319,21 +319,23 @@ static void finish_if_sparse(struct progress *progress)\n>\n>  void stop_progress(struct progress **p_progress)\n>  {\n> +\tstruct progress *progress;\n>  \tif (!p_progress)\n>  \t\tBUG(\"don't provide NULL to stop_progress\");\n> +\tprogress = *p_progress;\n>\n> -\tfinish_if_sparse(*p_progress);\n> +\tfinish_if_sparse(progress);\n>\n> -\tif (*p_progress) {\n> +\tif (progress) {\n>  \t\ttrace2_data_intmax(\"progress\", the_repository, \"total_objects\",\n> -\t\t\t\t   (*p_progress)->total);\n> +\t\t\t\t   progress->total);\n>\n> -\t\tif ((*p_progress)->throughput)\n> +\t\tif (progress->throughput)\n>  \t\t\ttrace2_data_intmax(\"progress\", the_repository,\n>  \t\t\t\t\t   \"total_bytes\",\n> -\t\t\t\t\t   (*p_progress)->throughput->curr_total);\n> +\t\t\t\t\t   progress->throughput->curr_total);\n>\n> -\t\ttrace2_region_leave(\"progress\", (*p_progress)->title, the_repository);\n> +\t\ttrace2_region_leave(\"progress\", progress->title, the_repository);\n>  \t}\n>\n>  \tstop_progress_msg(p_progress, _(\"done\"));\n\nThis patch is trivially correct, but I wonder why all that code is here\ninstead of in stop_progress_msg().  I would expect stop_progress() to be\na thin wrapper that just provides a default message, but actually it\nhandles sparse progress and tracing.  Isn't both necessary even with a\ncustom message?\n\nIn any case, moving the code there becomes easier after this patch.\n\nRené\n"},{"id":"445107","messageId":"20211228161350.mzsubl3vensst2hu@gmail.com","threadId":"56641","inReplyTo":"patch-v8-5.7-72a31bd7191-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 5/7] progress.c: add temporary variable from progress struct","fromName":"Johannes Altmanninger","fromEmail":"aclopte@gmail.com","sentAt":"2021-12-28T16:13:50Z","receivedAt":"2021-12-28T16:13:56Z","isPatch":true,"sender":{"key":"aclopte@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6853872?v=4"},"body":"On Tue, Dec 28, 2021 at 04:19:01PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Since 98a13647408 (trace2: log progress time and throughput,\n> 2020-05-12) stop_progress() dereferences a \"struct progress **\"\n> parameter in several places. Extract a dereferenced variable (like in\n> stop_progress_msg()) to reduce clutter and make it clearer who needs\n\nThe \"(like in stop_progress_msg())\" can probably go because you explain the\nadded consistency in the next paragraph.\n\n> to write to this parameter.\n> \n> Now instead of using \"*p_progress\" several times in stop_progress() we\n> check it once for NULL and then use a dereferenced \"progress\" variable\n> thereafter. This continues the same pattern used in the above\n> stop_progress() function, see ac900fddb7f (progress: don't dereference\n\n\"above stop_progress\" should be \"below stop_progress_msg\",\nbecause stop_progress is the one you're modifying?\n\n> before checking for NULL, 2020-08-10).\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  progress.c | 14 ++++++++------\n>  1 file changed, 8 insertions(+), 6 deletions(-)\n> \n> diff --git a/progress.c b/progress.c\n> index 680c6a8bf93..688749648be 100644\n> --- a/progress.c\n> +++ b/progress.c\n> @@ -319,21 +319,23 @@ static void finish_if_sparse(struct progress *progress)\n>  \n>  void stop_progress(struct progress **p_progress)\n>  {\n> +\tstruct progress *progress;\n\nnit: in stop_progress_msg we have a blank line here, the inconsistency is\nmildly surprising\n\n>  \tif (!p_progress)\n>  \t\tBUG(\"don't provide NULL to stop_progress\");\n> +\tprogress = *p_progress;\n>  \n> -\tfinish_if_sparse(*p_progress);\n> +\tfinish_if_sparse(progress);\n>  \n> -\tif (*p_progress) {\n> +\tif (progress) {\n>  \t\ttrace2_data_intmax(\"progress\", the_repository, \"total_objects\",\n> -\t\t\t\t   (*p_progress)->total);\n> +\t\t\t\t   progress->total);\n>  \n> -\t\tif ((*p_progress)->throughput)\n> +\t\tif (progress->throughput)\n>  \t\t\ttrace2_data_intmax(\"progress\", the_repository,\n>  \t\t\t\t\t   \"total_bytes\",\n> -\t\t\t\t\t   (*p_progress)->throughput->curr_total);\n> +\t\t\t\t\t   progress->throughput->curr_total);\n>  \n> -\t\ttrace2_region_leave(\"progress\", (*p_progress)->title, the_repository);\n> +\t\ttrace2_region_leave(\"progress\", progress->title, the_repository);\n>  \t}\n>  \n>  \tstop_progress_msg(p_progress, _(\"done\"));\n> -- \n> 2.34.1.1257.g2af47340c7b\n> \n"},{"id":"445109","messageId":"20211228162546.quzsbwgvot7o7z76@gmail.com","threadId":"56641","inReplyTo":"patch-v8-3.7-271f6d7ec3b-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 3/7] progress.c tests: make start/stop commands on stdin","fromName":"Johannes Altmanninger","fromEmail":"aclopte@gmail.com","sentAt":"2021-12-28T16:25:46Z","receivedAt":"2021-12-28T16:25:52Z","isPatch":true,"sender":{"key":"aclopte@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6853872?v=4"},"body":"On Tue, Dec 28, 2021 at 04:18:59PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Change the usage of the \"test-tool progress\" introduced in\n> 2bb74b53a49 (Test the progress display, 2019-09-16) to take command\n> like \"start\" and \"stop\" on stdin, instead of running them implicitly.\n> \n> This makes for tests that are easier to read, since the recipe will\n> mirror the API usage, and allows for easily testing invalid usage that\n> would yield (or should yield) a BUG(), e.g. providing two \"start\"\n> calls in a row. A subsequent commit will add such tests.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/helper/test-progress.c    | 46 ++++++++++++++++++++++-------\n>  t/t0500-progress-display.sh | 58 +++++++++++++++++++++++--------------\n>  2 files changed, 72 insertions(+), 32 deletions(-)\n> \n> diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c\n> index 50fd3be3dad..becc163375f 100644\n> --- a/t/helper/test-progress.c\n> +++ b/t/helper/test-progress.c\n> @@ -3,6 +3,9 @@\n>   *\n>   * Reads instructions from standard input, one instruction per line:\n>   *\n> + *   \"start <total>[ <title>]\" - Call start_progress(title, total),\n> + *                               Uses the default title of \"Working hard\"\n> + *                               if the \" <title>\" is omitted.\n>   *   \"progress <items>\" - Call display_progress() with the given item count\n>   *                        as parameter.\n>   *   \"throughput <bytes> <millis> - Call display_throughput() with the given\n> @@ -10,6 +13,7 @@\n>   *                                  specify the time elapsed since the\n>   *                                  start_progress() call.\n>   *   \"update\" - Set the 'progress_update' flag.\n> + *   \"stop\" - Call stop_progress().\n>   *\n>   * See 't0500-progress-display.sh' for examples.\n>   */\n> @@ -19,34 +23,52 @@\n>  #include \"parse-options.h\"\n>  #include \"progress.h\"\n>  #include \"strbuf.h\"\n> +#include \"string-list.h\"\n>  \n>  int cmd__progress(int argc, const char **argv)\n>  {\n> -\tint total = 0;\n> -\tconst char *title;\n> +\tconst char *const default_title = \"Working hard\";\n> +\tstruct string_list titles = STRING_LIST_INIT_DUP;\n>  \tstruct strbuf line = STRBUF_INIT;\n> -\tstruct progress *progress;\n> +\tstruct progress *progress = NULL;\n>  \n>  \tconst char *usage[] = {\n> -\t\t\"test-tool progress [--total=<n>] <progress-title>\",\n> +\t\t\"test-tool progress <stdin\",\n>  \t\tNULL\n>  \t};\n>  \tstruct option options[] = {\n> -\t\tOPT_INTEGER(0, \"total\", &total, \"total number of items\"),\n>  \t\tOPT_END(),\n>  \t};\n>  \n>  \targc = parse_options(argc, argv, NULL, options, usage, 0);\n> -\tif (argc != 1)\n> -\t\tdie(\"need a title for the progress output\");\n> -\ttitle = argv[0];\n> +\tif (argc)\n> +\t\tusage_with_options(usage, options);\n>  \n>  \tprogress_testing = 1;\n> -\tprogress = start_progress(title, total);\n>  \twhile (strbuf_getline(&line, stdin) != EOF) {\n>  \t\tchar *end;\n>  \n> -\t\tif (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n> +\t\tif (skip_prefix(line.buf, \"start \", (const char **) &end)) {\n> +\t\t\tuint64_t total = strtoull(end, &end, 10);\n> +\t\t\tconst char *title;\n> +\t\t\tconst char *str;\n> +\n> +\t\t\t/*\n> +\t\t\t * We can't use \"end + 1\" as an argument to\n> +\t\t\t * start_progress(), it doesn't xstrdup() its\n> +\t\t\t * \"title\" argument. We need to hold onto a\n> +\t\t\t * valid \"char *\" for it until the end.\n> +\t\t\t */\n> +\t\t\tif (!*end)\n> +\t\t\t\ttitle = default_title;\n> +\t\t\telse if (*end == ' ')\n> +\t\t\t\ttitle = string_list_insert(&titles, end + 1)->string;\n> +\t\t\telse\n> +\t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n> +\n> +\t\t\tstr = title ? title : default_title;\n\nI don't think title is ever NULL, so we should be able to elide this variable.\n(Did you want to fall back to the default title when the input is \"start \"?)\n\n> +\t\t\tprogress = start_progress(str, total);\n> +\t\t} else if (skip_prefix(line.buf, \"progress \", (const char **) &end)) {\n>  \t\t\tuint64_t item_count = strtoull(end, &end, 10);\n>  \t\t\tif (*end != '\\0')\n>  \t\t\t\tdie(\"invalid input: '%s'\\n\", line.buf);\n"},{"id":"445112","messageId":"20211228163324.xolh4lbsxuv4k54x@gmail.com","threadId":"56641","inReplyTo":"patch-v8-4.7-7c1b8b287c5-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 4/7] progress.c tests: test some invalid usage","fromName":"Johannes Altmanninger","fromEmail":"aclopte@gmail.com","sentAt":"2021-12-28T16:33:24Z","receivedAt":"2021-12-28T16:33:30Z","isPatch":true,"sender":{"key":"aclopte@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6853872?v=4"},"body":"On Tue, Dec 28, 2021 at 04:19:00PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Test what happens when we \"stop\" without a \"start\", omit the \"stop\"\n> after a \"start\", or try to start two concurrent progress bars. This\n\nI think there is still no test for the two concurrent progress bars,\nbut you mention it here, and also in the previous patch's message,\nwhich is misleading.\n"},{"id":"445113","messageId":"d0bdc7e5-9e34-49d8-33cf-dd96a807617e@web.de","threadId":"56641","inReplyTo":"patch-v8-7.7-060483fb5ce-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 7/7] *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2021-12-28T16:47:11Z","receivedAt":"2021-12-28T16:47:20Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 28.12.21 um 16:19 schrieb Ævar Arnfjörð Bjarmason:\n> We have over 50 uses of \"isatty(1)\" and \"isatty(2)\" in the codebase,\n> and around 10 \"isatty(0)\", but three callers used the\n> {STDIN_FILENO,STD{OUT,ERR}_FILENO} macros in \"stdlib.h\" to refer to\n> them.\n>\n> Let's change these for consistency.  This makes it easier to change\n> all calls to isatty() at a whim, which is useful to test some\n> scenarios[1].\n\nHmm.  Matching e.g. \"(0|STDIN_FILENO)\" instead of \"0\" is harder, of\ncourse, but not much.\n\nShouldn't we use these macros more to reduce the number of magic values?\nThe code is slightly easier to read before this patch because it doesn't\nrequire the reader to know the meaning of these numbers.\n\nReducing the constants to their numerical values is easy to automate in\ngeneral; the opposite direction is harder.  Coccinelle can help us take\nsuch a step with a semantic patch like this:\n\n\t@@\n\t@@\n\t  isatty(\n\t(\n\t- 0\n\t+ STDIN_FILENO\n\t|\n\t- 1\n\t+ STDOUT_FILENO\n\t|\n\t- 2\n\t+ STDERR_FILENO\n\t)\n\t  )\n\n>\n> 1. https://lore.kernel.org/git/patch-v6-8.8-bff919994b5-20211102T122507Z-avarab@gmail.com/\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/bisect--helper.c | 2 +-\n>  builtin/bundle.c         | 2 +-\n>  compat/mingw.c           | 2 +-\n>  3 files changed, 3 insertions(+), 3 deletions(-)\n>\n> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\n> index 28a2e6a5750..21360a4e70b 100644\n> --- a/builtin/bisect--helper.c\n> +++ b/builtin/bisect--helper.c\n> @@ -830,7 +830,7 @@ static int bisect_autostart(struct bisect_terms *terms)\n>  \tfprintf_ln(stderr, _(\"You need to start by \\\"git bisect \"\n>  \t\t\t  \"start\\\"\\n\"));\n>\n> -\tif (!isatty(STDIN_FILENO))\n> +\tif (!isatty(0))\n>  \t\treturn -1;\n>\n>  \t/*\n> diff --git a/builtin/bundle.c b/builtin/bundle.c\n> index 5a85d7cd0fe..df69c651753 100644\n> --- a/builtin/bundle.c\n> +++ b/builtin/bundle.c\n> @@ -56,7 +56,7 @@ static int parse_options_cmd_bundle(int argc,\n>\n>  static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {\n>  \tint all_progress_implied = 0;\n> -\tint progress = isatty(STDERR_FILENO);\n> +\tint progress = isatty(2);\n>  \tstruct strvec pack_opts;\n>  \tint version = -1;\n>  \tint ret;\n> diff --git a/compat/mingw.c b/compat/mingw.c\n> index e14f2d5f77c..7c55d0f0414 100644\n> --- a/compat/mingw.c\n> +++ b/compat/mingw.c\n> @@ -2376,7 +2376,7 @@ int mingw_raise(int sig)\n>  \tswitch (sig) {\n>  \tcase SIGALRM:\n>  \t\tif (timer_fn == SIG_DFL) {\n> -\t\t\tif (isatty(STDERR_FILENO))\n> +\t\t\tif (isatty(2))\n>  \t\t\t\tfputs(\"Alarm clock\\n\", stderr);\n>  \t\t\texit(128 + SIGALRM);\n>  \t\t} else if (timer_fn != SIG_IGN)\n"},{"id":"445156","messageId":"211229.86zgokgttz.gmgdl@evledraar.gmail.com","threadId":"56641","inReplyTo":"d0bdc7e5-9e34-49d8-33cf-dd96a807617e@web.de","subject":"Re: [PATCH v8 7/7] *.c: use isatty(0|2), not isatty(STDIN_FILENO|STDERR_FILENO)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-12-28T23:56:18Z","receivedAt":"2021-12-29T00:04:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Dec 28 2021, René Scharfe wrote:\n\n> Am 28.12.21 um 16:19 schrieb Ævar Arnfjörð Bjarmason:\n>> We have over 50 uses of \"isatty(1)\" and \"isatty(2)\" in the codebase,\n>> and around 10 \"isatty(0)\", but three callers used the\n>> {STDIN_FILENO,STD{OUT,ERR}_FILENO} macros in \"stdlib.h\" to refer to\n>> them.\n>>\n>> Let's change these for consistency.  This makes it easier to change\n>> all calls to isatty() at a whim, which is useful to test some\n>> scenarios[1].\n>\n> Hmm.  Matching e.g. \"(0|STDIN_FILENO)\" instead of \"0\" is harder, of\n> course, but not much.\n>\n> Shouldn't we use these macros more to reduce the number of magic values?\n> The code is slightly easier to read before this patch because it doesn't\n> require the reader to know the meaning of these numbers.\n>\n> Reducing the constants to their numerical values is easy to automate in\n> general; the opposite direction is harder.  Coccinelle can help us take\n> such a step with a semantic patch like this:\n>\n> \t@@\n> \t@@\n> \t  isatty(\n> \t(\n> \t- 0\n> \t+ STDIN_FILENO\n> \t|\n> \t- 1\n> \t+ STDOUT_FILENO\n> \t|\n> \t- 2\n> \t+ STDERR_FILENO\n> \t)\n> \t  )\n\nWe don't bother with EXIT_SUCCESS and EXIT_FAILURE, and for those (VMS)\nthere is a reason to not use the constants, as EXIT_FAILURE may differ.\n\nBut for these I personally think these symbolic names are rather\nuseless.\n\nThey never differ, and when working on POSIX systems you're going to\nneed to know that 1 is stdout, 2 is stderr. You're also going to have to\nmaintain shellscripts that use \">&2\" or whatever. Those aren't using a\nhypothetical \">&$STDERR_FILENO\".\n\nBut in any case, this change isn't even trying to make the argument that\nwe *should* use one over the other, just that the constants are used\nmuch more than *_FILENO, so changing them to make a subsequent (now\nejected out of this series) change easier to explain is worth it.\n\nSo I'd think we can just take this small change, and argue separately\nwhether it's worth it to apply that coccinelle rule.\n"},{"id":"445240","messageId":"xmqqv8z5py42.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v6-4.6-b5aa6e266f6-20211228T143223Z-avarab@gmail.com","subject":"Re: [PATCH v6 4/6] object-name: show date for ambiguous tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-30T21:43:41Z","receivedAt":"2021-12-30T21:43:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/object-name.c b/object-name.c\n> index dcf3ab99990..990f384129e 100644\n> --- a/object-name.c\n> +++ b/object-name.c\n> @@ -403,21 +403,26 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n>  \t} else if (type == OBJ_TAG) {\n>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>  \t\tconst char *tag_tag = \"\";\n> +\t\ttimestamp_t tag_date = 0;\n>  \n> -\t\tif (!parse_tag(tag) && tag->tag)\n> +\t\tif (!parse_tag(tag) && tag->tag) {\n>  \t\t\ttag_tag = tag->tag;\n> +\t\t\ttag_date = tag->date;\n> +\t\t}\n>  \n>  \t\t/*\n>  \t\t * TRANSLATORS: This is a line of\n>  \t\t * ambiguous tag object output. E.g.:\n>  \t\t *\n> -\t\t *    \"deadbeef tag Some Tag Message\"\n> +\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n>  \t\t *\n>  \t\t * The second argument is the \"tag\" string from\n>  \t\t * object.c, it should (hopefully) already be\n>  \t\t * translated.\n>  \t\t */\n> -\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n> +\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n> +\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n> +\t\t\t    tag_tag);\n\nSo, when parse_tag() errors out, we show \"\" and epoch?  We should be\nable to do a better error reporting than that; tag_tag and tag_date\nare both local and they do not have to be used to store sentinel values\nlike that.  Instead perhaps remember that we failed to parse_tag(),\nand _omit_ unavailable piece of information from the output?  I dunno.\n\n>  \t} else if (type == OBJ_TREE) {\n>  \t\t/*\n>  \t\t * TRANSLATORS: This is a line of ambiguous <type>\n"},{"id":"445262","messageId":"xmqqv8z5mzqt.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v6-1.6-27f267ad555-20211228T143223Z-avarab@gmail.com","subject":"Re: [PATCH v6 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-30T23:36:42Z","receivedAt":"2021-12-30T23:36:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +test_cmp_failed_rev_parse () {\n> +\tdir=$1\n> +\trev=$2\n> +\tshift\n\nWhat are we shifting away?\n\n> +\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n> +\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../g\" <actual.raw >actual &&\n\nI wonder if we need to ensure not to mistakenly produce second hit\nin an object name that has $rev twice, e.g. \"cafe123cafe...\"?\n\n> +\ttest_cmp expect actual\n> +}\n\nIt is a bit confusing to _depend_ on the caller to prepare a\nfixed-name file, like this.  We've avoided such confusion in\ndifferent ways in other tests, like (A) make the helper take\nthe expected output from its standard input, or (B) make the\nhelper take the name of the file that has expected output as\nits argument.\n\n> +test_expect_success 'ambiguous blob output' '\n> +\tgit init --bare blob.prefix &&\n> +\t(\n> +\t\tcd blob.prefix &&\n> +\n> +\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n> +\t\techo brocdnra | git hash-object -w --stdin &&\n> +\t\techo brigddsv | git hash-object -w --stdin &&\n> +\n> +\t\t# Both start with \"beef..\"\n> +\t\techo 1agllotbh | git hash-object -w --stdin &&\n> +\t\techo 1bbfctrkc | git hash-object -w --stdin\n> +\t) &&\n> +\n> +\ttest_must_fail git -C blob.prefix rev-parse dead &&\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID beef... is ambiguous\n> +\thint: The candidates are:\n> +\thint:   beef... blob\n> +\thint:   beef... blob\n> +\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n> +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n> +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.prefix beef\n> +'\n> +\n> +test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n\n\"loose bad object\", as they aren't even blobs, perhaps?\n\n> +\tgit init --bare blob.bad &&\n> +\t(\n> +\t\tcd blob.bad &&\n> +\n> +\t\t# Both have the prefix \"bad0\"\n> +\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n> +\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n> +\t) &&\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID bad0... is ambiguous\n> +\thint: The candidates are:\n> +\tfatal: invalid object type\n\nThat indeed is not very nice.\n\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.bad bad0\n> +'\n> +\n> +test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n> +\tgit init --bare blob.corrupt &&\n> +\t(\n> +\t\tcd blob.corrupt &&\n> +\n> +\t\t# Both have the prefix \"cafe\"\n> +\t\techo bnkxmdwz | git hash-object -w --stdin &&\n> +\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n> +\n> +\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n> +\t\tchmod 755 $oidf &&\n> +\t\techo broken >$oidf\n> +\t) &&\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\terror: short object ID cafe... is ambiguous\n> +\thint: The candidates are:\n> +\terror: inflate: data stream error (incorrect header check)\n> +\terror: unable to unpack cafe... header\n> +\terror: inflate: data stream error (incorrect header check)\n> +\terror: unable to unpack cafe... header\n> +\thint:   cafe... unknown type\n> +\thint:   cafe... blob\n\nThis is an interesting one.  I _think_ it is clear enough for the\nreaders that the inflate errors are for the object that immediately\nfollows them, so as long as we show these hints one by one, the\nabove output is perfectly fine.  But we'll see.\n\n> +\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n> +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n> +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n> +\tEOF\n> +\ttest_cmp_failed_rev_parse blob.corrupt cafe\n> +'\n> +\n>  if ! test_have_prereq SHA1\n>  then\n>  \tskip_all='not using SHA-1 for objects'\n"},{"id":"445263","messageId":"xmqqpmpdmza5.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v6-3.6-daebc95542c-20211228T143223Z-avarab@gmail.com","subject":"Re: [PATCH v6 3/6] object-name: make ambiguous object output translatable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-12-30T23:46:42Z","receivedAt":"2021-12-30T23:46:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +\t\t/*\n> +\t\t * TRANSLATORS: This is a line of\n> +\t\t * ambiguous tag object output. E.g.:\n> +\t\t *\n> +\t\t *    \"deadbeef tag Some Tag Message\"\n> +\t\t *\n> +\t\t * The second argument is the \"tag\" string from\n> +\t\t * object.c, it should (hopefully) already be\n> +\t\t * translated.\n> +\t\t */\n> +\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n\nIt is better to lose \", it should (hopefully) already be translated\"\nnear the end of the comment.\n\n> +\t} else if (type == OBJ_TREE) {\n> +\t\t/*\n> +\t\t * TRANSLATORS: This is a line of ambiguous <type>\n> +\t\t * object output. E.g. \"deadbeef tree\".\n> +\t\t */\n> +\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n> +\t} else if (type == OBJ_BLOB) {\n> +\t\t/*\n> +\t\t * TRANSLATORS: This is a line of ambiguous <type>\n> +\t\t * object output. E.g. \"deadbeef blob\".\n> +\t\t */\n> +\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n>  \t}\n>  \n> +\n>  out:\n> -\tadvise(\"  %s %s\",\n> -\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n> -\t       desc.buf);\n> +\t/*\n> +\t * TRANSLATORS: This is line item of ambiguous object output\n> +\t * from describe_ambiguous_object() above.\n> +\t */\n> +\tadvise(_(\"  %s\"), desc.buf);\n\nWhat do we expect the translators to do here?  Swap order of the\nleading space and the string around?\n\nAll the other sentence legos we see in the earlier part of this\npatch (omitted) looked quite sensibly done.  Especially the part\nthat shows a commit object, which lets the translators to take the\nobject name, the date string, and the message and combine them into\na single string in an order of their choice is nice.\n"},{"id":"445766","messageId":"xmqqwnjb3vjj.fsf@gitster.g","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com","subject":"Re: [PATCH v8 0/7] progress: test fixes / cleanup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-08T00:45:04Z","receivedAt":"2022-01-08T00:45:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Various test, leak and other fixes for the progress.c code and its\n> tests. This v8 addresses feedback on v7[1] by Johannes\n> Altmanninger. For that round I accidentally broke the In-Reply-To\n> chain, so I'm replying to the v6 here to attach it to the original\n> thread again.\n\nIs this replying to v6 of a totally unrelated topic that is about\nambiguous object name?\n\n"},{"id":"445993","messageId":"patch-v7-1.6-28c01b7f8a5-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:20Z","receivedAt":"2022-01-12T12:40:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the tests for ambiguous objects to check how we handle objects\nwhere we return OBJ_BAD when trying to parse them. As noted in [1] we\nhave a blindspot when it comes to this behavior.\n\nSince we need to add new test data here let's extend these tests to be\ntested under SHA-256, in d7a2fc82491 (t1512: skip test if not using\nSHA-1, 2018-05-13) all of the existing tests were skipped, as they\nrely on specific SHA-1 object IDs.\n\nFor these tests it only matters that the first 4 characters of the OID\nprefix are the same for both SHA-1 and SHA-256. This uses strings that\nI mined, and have the same prefix when hashed with both.\n\nWe \"test_cmp\" the full output to guard against any future regressions,\nand because a subsequent commit will tweak it. Showing a diff of how\nthe output changes is helpful to explain those subsequent commits.\n\n1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1512-rev-parse-disambiguation.sh | 79 +++++++++++++++++++++++++++++\n 1 file changed, 79 insertions(+)\n\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex b0119bf8bc8..01feeeafb72 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -25,6 +25,85 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+test_cmp_failed_rev_parse () {\n+\tcat >expect &&\n+\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n+\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ambiguous blob output' '\n+\tgit init --bare blob.prefix &&\n+\t(\n+\t\tcd blob.prefix &&\n+\n+\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n+\t\techo brocdnra | git hash-object -w --stdin &&\n+\t\techo brigddsv | git hash-object -w --stdin &&\n+\n+\t\t# Both start with \"beef..\"\n+\t\techo 1agllotbh | git hash-object -w --stdin &&\n+\t\techo 1bbfctrkc | git hash-object -w --stdin\n+\t) &&\n+\n+\ttest_must_fail git -C blob.prefix rev-parse dead &&\n+\ttest_cmp_failed_rev_parse blob.prefix beef <<-\\EOF\n+\terror: short object ID beef... is ambiguous\n+\thint: The candidates are:\n+\thint:   beef... blob\n+\thint:   beef... blob\n+\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+'\n+\n+test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n+\tgit init --bare blob.bad &&\n+\t(\n+\t\tcd blob.bad &&\n+\n+\t\t# Both have the prefix \"bad0\"\n+\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n+\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n+\t) &&\n+\n+\ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n+\terror: short object ID bad0... is ambiguous\n+\thint: The candidates are:\n+\tfatal: invalid object type\n+\tEOF\n+'\n+\n+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n+\tgit init --bare blob.corrupt &&\n+\t(\n+\t\tcd blob.corrupt &&\n+\n+\t\t# Both have the prefix \"cafe\"\n+\t\techo bnkxmdwz | git hash-object -w --stdin &&\n+\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n+\n+\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n+\t\tchmod 755 $oidf &&\n+\t\techo broken >$oidf\n+\t) &&\n+\n+\ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n+\terror: short object ID cafe... is ambiguous\n+\thint: The candidates are:\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\thint:   cafe... unknown type\n+\thint:   cafe... blob\n+\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+'\n+\n if ! test_have_prereq SHA1\n then\n \tskip_all='not using SHA-1 for objects'\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445994","messageId":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com","subject":"[PATCH v7 0/6] object-name: make ambiguous object output translatable + show tag date","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:19Z","receivedAt":"2022-01-12T12:40:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This topic improves the output we emit on ambiguous objects as noted\nin 4/6, and makes it translatable, see 3/6. See [1] for v6.\n\nThis v7 addresses all the feedback on v7 from Junio. Note also that\nthere's an unrelated v8[2] in reply to the v6 from another topic,\nbecause I mixed up the In-Reply-To for the two while submitting a\nre-roll of it, sorry about that.\n\n1. https://lore.kernel.org/git/cover-v6-0.6-00000000000-20211228T143223Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/cover-v8-0.7-00000000000-20211228T150728Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (6):\n  object-name tests: add tests for ambiguous object blind spots\n  object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n  object-name: iterate ambiguous objects before showing header\n  object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n\n object-name.c                       | 113 +++++++++++++++++++++++++---\n t/t1512-rev-parse-disambiguation.sh |  78 +++++++++++++++++++\n 2 files changed, 179 insertions(+), 12 deletions(-)\n\nRange-diff against v6:\n1:  27f267ad555 ! 1:  28c01b7f8a5 object-name tests: add tests for ambiguous object blind spots\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      . ./test-lib.sh\n      \n     +test_cmp_failed_rev_parse () {\n    -+\tdir=$1\n    -+\trev=$2\n    -+\tshift\n    -+\n    -+\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n    -+\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../g\" <actual.raw >actual &&\n    ++\tcat >expect &&\n    ++\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n    ++\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n     +\ttest_cmp expect actual\n     +}\n     +\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\t) &&\n     +\n     +\ttest_must_fail git -C blob.prefix rev-parse dead &&\n    -+\tcat >expect <<-\\EOF &&\n    ++\ttest_cmp_failed_rev_parse blob.prefix beef <<-\\EOF\n     +\terror: short object ID beef... is ambiguous\n     +\thint: The candidates are:\n     +\thint:   beef... blob\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n     +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n     +\tEOF\n    -+\ttest_cmp_failed_rev_parse blob.prefix beef\n     +'\n     +\n    -+test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n    ++test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n     +\tgit init --bare blob.bad &&\n     +\t(\n     +\t\tcd blob.bad &&\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n     +\t) &&\n     +\n    -+\tcat >expect <<-\\EOF &&\n    ++\ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n     +\terror: short object ID bad0... is ambiguous\n     +\thint: The candidates are:\n     +\tfatal: invalid object type\n     +\tEOF\n    -+\ttest_cmp_failed_rev_parse blob.bad bad0\n     +'\n     +\n     +test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\t\techo broken >$oidf\n     +\t) &&\n     +\n    -+\tcat >expect <<-\\EOF &&\n    ++\ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n     +\terror: short object ID cafe... is ambiguous\n     +\thint: The candidates are:\n     +\terror: inflate: data stream error (incorrect header check)\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     +\tUse '\\''--'\\'' to separate paths from revisions, like this:\n     +\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n     +\tEOF\n    -+\ttest_cmp_failed_rev_parse blob.corrupt cafe\n     +'\n     +\n      if ! test_have_prereq SHA1\n2:  c78243dc701 = 2:  b7027dfc843 object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n3:  daebc95542c ! 3:  65801f2c890 object-name: make ambiguous object output translatable\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\t *    \"deadbeef tag Some Tag Message\"\n     +\t\t *\n     +\t\t * The second argument is the \"tag\" string from\n    -+\t\t * object.c, it should (hopefully) already be\n    -+\t\t * translated.\n    ++\t\t * object.c.\n     +\t\t */\n     +\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n     +\t} else if (type == OBJ_TREE) {\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     -\t       desc.buf);\n     +\t/*\n     +\t * TRANSLATORS: This is line item of ambiguous object output\n    -+\t * from describe_ambiguous_object() above.\n    ++\t * from describe_ambiguous_object() above. For RTL languages\n    ++\t * you'll probably want to swap the \"%s\" and leading \" \" space\n    ++\t * around.\n     +\t */\n     +\tadvise(_(\"  %s\"), desc.buf);\n      \n4:  b5aa6e266f6 ! 4:  2e5511c9fa5 object-name: show date for ambiguous tag objects\n    @@ Commit message\n     \n             hint:   b7e68c41d92 tag v2.32.0\n     \n    +    As with OBJ_COMMIT we punt on the cases where the date in the object\n    +    is nonsensical, and other cases where parse_tag() might fail. For\n    +    those we'll use our default date of \"0\" and tag message of\n    +    \"\". E.g. for some of the corrupt tags created by t3800-mktag.sh we'd\n    +    emit a line like:\n    +\n    +        hint:   8d62cb0b06 tag 1970-01-01 -\n    +\n    +    We could detect that and emit a \"%s [bad tag object]\" message (to go\n    +    with the existing generic \"%s [bad object]\"), but I don't think it's\n    +    worth the effort. Users are unlikely to ever run into cases where\n    +    they've got a broken object that's also ambiguous, and in case they do\n    +    output that's a bit nonsensical beats wasting translator time on this\n    +    obscure edge case.\n    +\n    +    We should instead change parse_tag_buffer() to be more eager to emit\n    +    an error() instead of silently aborting with \"return -1;\". In the case\n    +    of \"t3800-mktag.sh\" it takes the \"size < the_hash_algo->hexsz + 24\"\n    +    branch.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## object-name.c ##\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n      \t\t *\n      \t\t * The second argument is the \"tag\" string from\n    - \t\t * object.c, it should (hopefully) already be\n    - \t\t * translated.\n    + \t\t * object.c.\n      \t\t */\n     -\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n     +\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n5:  644b076b2a6 ! 5:  2c03cdd3c1e object-name: iterate ambiguous objects before showing header\n    @@ object-name.c: static int init_object_disambiguation(struct repository *r,\n      \tint type;\n      \tconst char *hash;\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - \t * TRANSLATORS: This is line item of ambiguous object output\n    - \t * from describe_ambiguous_object() above.\n    + \t * you'll probably want to swap the \"%s\" and leading \" \" space\n    + \t * around.\n      \t */\n     -\tadvise(_(\"  %s\"), desc.buf);\n     +\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n    @@ object-name.c: static enum get_oid_result get_short_oid(struct repository *r,\n      \treturn status;\n     \n      ## t/t1512-rev-parse-disambiguation.sh ##\n    -@@ t/t1512-rev-parse-disambiguation.sh: test_expect_success 'ambiguous loose blob parsed as OBJ_BAD' '\n    +@@ t/t1512-rev-parse-disambiguation.sh: test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n      \n    - \tcat >expect <<-\\EOF &&\n    + \ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n      \terror: short object ID bad0... is ambiguous\n     -\thint: The candidates are:\n      \tfatal: invalid object type\n      \tEOF\n    - \ttest_cmp_failed_rev_parse blob.bad bad0\n    + '\n     @@ t/t1512-rev-parse-disambiguation.sh: test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n      \n    - \tcat >expect <<-\\EOF &&\n    + \ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n      \terror: short object ID cafe... is ambiguous\n     -\thint: The candidates are:\n      \terror: inflate: data stream error (incorrect header check)\n6:  6a31cfcfc29 ! 6:  bf226f67099 object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \t\tstrbuf_release(&date);\n      \t\tstrbuf_release(&msg);\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - \t\t * object.c, it should (hopefully) already be\n    - \t\t * translated.\n    + \t\t * The second argument is the \"tag\" string from\n    + \t\t * object.c.\n      \t\t */\n     -\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n     +\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \n      \n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - \t * TRANSLATORS: This is line item of ambiguous object output\n    - \t * from describe_ambiguous_object() above.\n    + \t * you'll probably want to swap the \"%s\" and leading \" \" space\n    + \t * around.\n      \t */\n     -\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n     +\tstrbuf_addf(advice, _(\"  %s\\n\"), sb->buf);\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445995","messageId":"patch-v7-2.6-b7027dfc843-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 2/6] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:21Z","receivedAt":"2022-01-12T12:40:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the \"unknown type\" handling in the code that displays the\nambiguous object list to assert() that we're either going to get the\n\"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\nreturn value from oid_object_info().\n\nSee [1] for the current output, and [1] for the commit that added the\n\"unknown type\" handling.\n\nWe are never going to get an \"unknown type\" in the sense of custom\ntypes crafted with \"hash-object --literally\", since we're not using\nthe OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n\nIf we manage to otherwise unpack such an object without errors we'll\ndie() in parse_loose_header_extended() called by sort_ambiguous()\nbefore we get to show_ambiguous_object(), as is asserted by the test\nadded in the preceding commit.\n\nSo saying \"unknown type\" here was always misleading, we really meant\nto say that we had a failure parsing the object at all, i.e. that we\nhad repository corruption. If the problem is only that it's type is\nunknown we won't reach this code.\n\nSo let's emit a generic \"[bad object]\" instead. As our tests added in\nthe preceding commit show, we'll have emitted various \"error\" output\nalready in those cases.\n\nWe should do better in the truly \"unknown type\" cases, which we'd need\nto handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\nflag. But let's leave that for some future improvement. In a\nsubsequent commit I'll improve the output we do show, and not having\nto handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\nsimplifies that change.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 14 ++++++++++++--\n t/t1512-rev-parse-disambiguation.sh |  2 +-\n 2 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..9750634ee76 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,16 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\n+\tif (type < 0) {\n+\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\tgoto out;\n+\t}\n+\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n+\tstrbuf_addstr(&desc, type_name(type));\n+\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -374,9 +384,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n \t}\n \n-\tadvise(\"  %s %s%s\",\n+out:\n+\tadvise(\"  %s %s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n \t       desc.buf);\n \n \tstrbuf_release(&desc);\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 01feeeafb72..5ed7e49edc7 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -96,7 +96,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n-\thint:   cafe... unknown type\n+\thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n \tUse '\\''--'\\'' to separate paths from revisions, like this:\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445996","messageId":"patch-v7-3.6-65801f2c890-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 3/6] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:22Z","receivedAt":"2022-01-12T12:40:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators.\n\nBy being able to customize the \"<SP><SP>%s\\n\" format we're even ready\nfor RTL languages, who'd presumably like to change that to\n\"%s<SP><SP>\\n\".\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n object-name.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------\n 1 file changed, 59 insertions(+), 7 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 9750634ee76..743f346842a 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,38 +356,90 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n \n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \n \tif (type < 0) {\n-\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous object\n+\t\t * output shown when we cannot look up or parse the\n+\t\t * object in question. E.g. \"deadbeef [bad object]\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n-\tstrbuf_addstr(&desc, type_name(type));\n \n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tstruct strbuf msg = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n+\t\t\t    hash, date.buf, msg.buf);\n+\n+\t\tstrbuf_release(&date);\n+\t\tstrbuf_release(&msg);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n+\t\tconst char *tag_tag = \"\";\n+\n \t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\t\ttag_tag = tag->tag;\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of\n+\t\t * ambiguous tag object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *\n+\t\t * The second argument is the \"tag\" string from\n+\t\t * object.c.\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n \t}\n \n+\n out:\n-\tadvise(\"  %s %s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output\n+\t * from describe_ambiguous_object() above. For RTL languages\n+\t * you'll probably want to swap the \"%s\" and leading \" \" space\n+\t * around.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445997","messageId":"patch-v7-4.6-2e5511c9fa5-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 4/6] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:23Z","receivedAt":"2022-01-12T12:40:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line of:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nAs with OBJ_COMMIT we punt on the cases where the date in the object\nis nonsensical, and other cases where parse_tag() might fail. For\nthose we'll use our default date of \"0\" and tag message of\n\"\". E.g. for some of the corrupt tags created by t3800-mktag.sh we'd\nemit a line like:\n\n    hint:   8d62cb0b06 tag 1970-01-01 -\n\nWe could detect that and emit a \"%s [bad tag object]\" message (to go\nwith the existing generic \"%s [bad object]\"), but I don't think it's\nworth the effort. Users are unlikely to ever run into cases where\nthey've got a broken object that's also ambiguous, and in case they do\noutput that's a bit nonsensical beats wasting translator time on this\nobscure edge case.\n\nWe should instead change parse_tag_buffer() to be more eager to emit\nan error() instead of silently aborting with \"return -1;\". In the case\nof \"t3800-mktag.sh\" it takes the \"size < the_hash_algo->hexsz + 24\"\nbranch.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 743f346842a..7c6cb60ceff 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -403,20 +403,25 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tconst char *tag_tag = \"\";\n+\t\ttimestamp_t tag_date = 0;\n \n-\t\tif (!parse_tag(tag) && tag->tag)\n+\t\tif (!parse_tag(tag) && tag->tag) {\n \t\t\ttag_tag = tag->tag;\n+\t\t\ttag_date = tag->date;\n+\t\t}\n \n \t\t/*\n \t\t * TRANSLATORS: This is a line of\n \t\t * ambiguous tag object output. E.g.:\n \t\t *\n-\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n \t\t *\n \t\t * The second argument is the \"tag\" string from\n \t\t * object.c.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n+\t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445998","messageId":"patch-v7-5.6-2c03cdd3c1e-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 5/6] object-name: iterate ambiguous objects before showing header","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:24Z","receivedAt":"2022-01-12T12:40:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"The candidates are\" header that's shown for ambiguous\nobjects to be shown after we've iterated over all of the objects.\n\nIf we get any errors while doing so we don't want to split up the the\nheader and the list as a result. The two will now be printed together,\nas shown in the updated testcase.\n\nAs we're accumulating the lines into as \"struct strbuf\" before\nemitting them we need to add a trailing newline to the call in\nshow_ambiguous_object(). This and the change from \"The candidates\nare:\" to \"The candidates are:\\n%s\" helps to give translators more\ncontext.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 27 +++++++++++++++++++++++----\n t/t1512-rev-parse-disambiguation.sh |  3 +--\n 2 files changed, 24 insertions(+), 6 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 7c6cb60ceff..71236ed1c16 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -351,9 +351,16 @@ static int init_object_disambiguation(struct repository *r,\n \treturn 0;\n }\n \n+struct ambiguous_output {\n+\tconst struct disambiguate_state *ds;\n+\tstruct strbuf advice;\n+};\n+\n static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n-\tconst struct disambiguate_state *ds = data;\n+\tstruct ambiguous_output *state = data;\n+\tconst struct disambiguate_state *ds = state->ds;\n+\tstruct strbuf *advice = &state->advice;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n \tconst char *hash;\n@@ -444,7 +451,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * you'll probably want to swap the \"%s\" and leading \" \" space\n \t * around.\n \t */\n-\tadvise(_(\"  %s\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n@@ -543,6 +550,10 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n+\t\tstruct ambiguous_output out = {\n+\t\t\t.ds = &ds,\n+\t\t\t.advice = STRBUF_INIT,\n+\t\t};\n \n \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n \n@@ -555,13 +566,21 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tif (!ds.ambiguous)\n \t\t\tds.fn = NULL;\n \n-\t\tadvise(_(\"The candidates are:\"));\n \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n \t\tsort_ambiguous_oid_array(r, &collect);\n \n-\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &out))\n \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n+\n+\t\t/*\n+\t\t * TRANSLATORS: The argument is the list of ambiguous\n+\t\t * objects composed in show_ambiguous_object(). See\n+\t\t * its \"TRANSLATORS\" comments for details.\n+\t\t */\n+\t\tadvise(_(\"The candidates are:\\n%s\"), out.advice.buf);\n+\n \t\toid_array_clear(&collect);\n+\t\tstrbuf_release(&out.advice);\n \t}\n \n \treturn status;\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 5ed7e49edc7..9c43699d3ae 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -70,7 +70,6 @@ test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n \n \ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n \terror: short object ID bad0... is ambiguous\n-\thint: The candidates are:\n \tfatal: invalid object type\n \tEOF\n '\n@@ -91,11 +90,11 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \n \ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n \terror: short object ID cafe... is ambiguous\n-\thint: The candidates are:\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n+\thint: The candidates are:\n \thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"445999","messageId":"patch-v7-6.6-bf226f67099-20220111T130811Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v7 6/6] object-name: re-use \"struct strbuf\" in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-12T12:39:25Z","receivedAt":"2022-01-12T12:40:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reduce the allocations done by show_ambiguous_object() by moving the\n\"desc\" strbuf into the \"struct ambiguous_output\" introduced in the\npreceding commit.\n\nThis doesn't matter for optimization purposes, but since we're\naccumulating a \"struct strbuf advice\" anyway let's follow that pattern\nand add a \"struct strbuf sb\", we can then strbuf_reset() it rather\nthan calling strbuf_release() for each call to\nshow_ambiguous_object().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 21 ++++++++++++---------\n 1 file changed, 12 insertions(+), 9 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 71236ed1c16..bce3f42356a 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -354,6 +354,7 @@ static int init_object_disambiguation(struct repository *r,\n struct ambiguous_output {\n \tconst struct disambiguate_state *ds;\n \tstruct strbuf advice;\n+\tstruct strbuf sb;\n };\n \n static int show_ambiguous_object(const struct object_id *oid, void *data)\n@@ -361,7 +362,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tstruct ambiguous_output *state = data;\n \tconst struct disambiguate_state *ds = state->ds;\n \tstruct strbuf *advice = &state->advice;\n-\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct strbuf *sb = &state->sb;\n \tint type;\n \tconst char *hash;\n \n@@ -377,7 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * output shown when we cannot look up or parse the\n \t\t * object in question. E.g. \"deadbeef [bad object]\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n@@ -402,8 +403,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t *\n \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n-\t\t\t    hash, date.buf, msg.buf);\n+\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, date.buf,\n+\t\t\t    msg.buf);\n \n \t\tstrbuf_release(&date);\n \t\tstrbuf_release(&msg);\n@@ -426,7 +427,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * The second argument is the \"tag\" string from\n \t\t * object.c.\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n \t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n \t\t\t    tag_tag);\n \t} else if (type == OBJ_TREE) {\n@@ -434,13 +435,13 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef tree\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s tree\"), hash);\n \t} else if (type == OBJ_BLOB) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef blob\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s blob\"), hash);\n \t}\n \n \n@@ -451,9 +452,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * you'll probably want to swap the \"%s\" and leading \" \" space\n \t * around.\n \t */\n-\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), sb->buf);\n \n-\tstrbuf_release(&desc);\n+\tstrbuf_reset(sb);\n \treturn 0;\n }\n \n@@ -552,6 +553,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n \t\tstruct ambiguous_output out = {\n \t\t\t.ds = &ds,\n+\t\t\t.sb = STRBUF_INIT,\n \t\t\t.advice = STRBUF_INIT,\n \t\t};\n \n@@ -581,6 +583,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \t\toid_array_clear(&collect);\n \t\tstrbuf_release(&out.advice);\n+\t\tstrbuf_release(&out.sb);\n \t}\n \n \treturn status;\n-- \n2.34.1.1373.g062f5534af2\n\n"},{"id":"446186","messageId":"xmqq8rvjgszp.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v7-1.6-28c01b7f8a5-20220111T130811Z-avarab@gmail.com","subject":"Re: [PATCH v7 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-13T22:39:54Z","receivedAt":"2022-01-13T22:40:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +test_cmp_failed_rev_parse () {\n> +\tcat >expect &&\n> +\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n> +\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n> +\ttest_cmp expect actual\n> +}\n\nThat's dense, especially without a comment (or named variable) that\nhints readers what the arguments to this helper (and its standard\ninput) ought to be.\n\nAs long as messages from rev-parse on the error stream never has\nmore than one abbreviated object name on a single line, the above\nshould give us a copy of the message with expected object name\nabbreviated to $2; otherwise we might be missing a /g in the sed\nscript.\n"},{"id":"446187","messageId":"xmqq1r1bgso2.fsf@gitster.g","threadId":"56641","inReplyTo":"patch-v7-4.6-2e5511c9fa5-20220111T130811Z-avarab@gmail.com","subject":"Re: [PATCH v7 4/6] object-name: show date for ambiguous tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-13T22:46:53Z","receivedAt":"2022-01-13T22:46:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  \t} else if (type == OBJ_TAG) {\n>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>  \t\tconst char *tag_tag = \"\";\n> +\t\ttimestamp_t tag_date = 0;\n\nHow about leaving these two uninitialized and introduce one extra\nbool,\n\t\tint tag_info_valid = 0;\n\nand then\n\n>  \n> -\t\tif (!parse_tag(tag) && tag->tag)\n> +\t\tif (!parse_tag(tag) && tag->tag) {\n>  \t\t\ttag_tag = tag->tag;\n> +\t\t\ttag_date = tag->date;\n\n\t\t\ttag_info_valid = 1;\n\n> +\t\t}\n>  \n>  \t\t/*\n>  \t\t * TRANSLATORS: This is a line of\n>  \t\t * ambiguous tag object output. E.g.:\n>  \t\t *\n> -\t\t *    \"deadbeef tag Some Tag Message\"\n> +\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n>  \t\t *\n>  \t\t * The second argument is the \"tag\" string from\n>  \t\t * object.c.\n>  \t\t */\n> -\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n> +\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n> +\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n> +\t\t\t    tag_tag);\n\nThen this part can use tag_info_valid to conditionally use tag_date\nand tag_tag:\n\n\t\tif (tag_info_valid)\n\t\t\tstrbuf_addf(&desc, ... <hash,date,tag>);\n\t\telse\n\t\t\tstrbuf_addf(&desc, _(\"%s tag [bad]\"), hash);\n\nwithout throwing a misleading \"In 1970 this happened\".\n\n>  \t} else if (type == OBJ_TREE) {\n>  \t\t/*\n>  \t\t * TRANSLATORS: This is a line of ambiguous <type>\n"},{"id":"446214","messageId":"220114.865yqmtt9z.gmgdl@evledraar.gmail.com","threadId":"56641","inReplyTo":"xmqq1r1bgso2.fsf@gitster.g","subject":"Re: [PATCH v7 4/6] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-14T12:05:45Z","receivedAt":"2022-01-14T12:07:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jan 13 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>>  \t} else if (type == OBJ_TAG) {\n>>  \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n>>  \t\tconst char *tag_tag = \"\";\n>> +\t\ttimestamp_t tag_date = 0;\n>\n> How about leaving these two uninitialized and introduce one extra\n> bool,\n> \t\tint tag_info_valid = 0;\n>\n> and then\n>\n>>  \n>> -\t\tif (!parse_tag(tag) && tag->tag)\n>> +\t\tif (!parse_tag(tag) && tag->tag) {\n>>  \t\t\ttag_tag = tag->tag;\n>> +\t\t\ttag_date = tag->date;\n>\n> \t\t\ttag_info_valid = 1;\n>\n>> +\t\t}\n>>  \n>>  \t\t/*\n>>  \t\t * TRANSLATORS: This is a line of\n>>  \t\t * ambiguous tag object output. E.g.:\n>>  \t\t *\n>> -\t\t *    \"deadbeef tag Some Tag Message\"\n>> +\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n>>  \t\t *\n>>  \t\t * The second argument is the \"tag\" string from\n>>  \t\t * object.c.\n>>  \t\t */\n>> -\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n>> +\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n>> +\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n>> +\t\t\t    tag_tag);\n>\n> Then this part can use tag_info_valid to conditionally use tag_date\n> and tag_tag:\n>\n> \t\tif (tag_info_valid)\n> \t\t\tstrbuf_addf(&desc, ... <hash,date,tag>);\n> \t\telse\n> \t\t\tstrbuf_addf(&desc, _(\"%s tag [bad]\"), hash);\n>\n> without throwing a misleading \"In 1970 this happened\".\n\nI still think the trade-off of not doing that discussed in the commit\nmessage is better, i.e. (to quote upthread):\n    \n    We could detect that and emit a \"%s [bad tag object]\" message (to go\n    with the existing generic \"%s [bad object]\"), but I don't think it's\n    worth the effort. Users are unlikely to ever run into cases where\n    they've got a broken object that's also ambiguous, and in case they do\n    output that's a bit nonsensical beats wasting translator time on this\n    obscure edge case.\n    \n    We should instead change parse_tag_buffer() to be more eager to emit\n    an error() instead of silently aborting with \"return -1;\". In the case\n    of \"t3800-mktag.sh\" it takes the \"size < the_hash_algo->hexsz + 24\"\n    branch.\n\nThis really is so obscure that I don't think it warrants having N\ntranslators re-translate this message users are very likely never to\nsee, ever.\n\nAnd to the extent that they will see anything I've got some\nplanned/upcoming changes to make some of the underlying object machinery\nemit better diagnostic messages on these bad objects, which would hint\nin the general case about what's going wrong, instead of needing\nambiguous-object-display-specific messaging.\n    \n>>  \t} else if (type == OBJ_TREE) {\n>>  \t\t/*\n>>  \t\t * TRANSLATORS: This is a line of ambiguous <type>\n\n"},{"id":"446215","messageId":"220114.861r1att7j.gmgdl@evledraar.gmail.com","threadId":"56641","inReplyTo":"xmqq8rvjgszp.fsf@gitster.g","subject":"Re: [PATCH v7 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-14T12:07:53Z","receivedAt":"2022-01-14T12:09:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jan 13 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> +test_cmp_failed_rev_parse () {\n>> +\tcat >expect &&\n>> +\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n>> +\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n>> +\ttest_cmp expect actual\n>> +}\n>\n> That's dense, especially without a comment (or named variable) that\n> hints readers what the arguments to this helper (and its standard\n> input) ought to be.\n\nI got rid of the named variables from v6 in response to a \"shift\" that\nshifted the wrong number, but perhaps I should have just removed the\n\"shift\"?\n\n> As long as messages from rev-parse on the error stream never has\n> more than one abbreviated object name on a single line, the above\n> should give us a copy of the message with expected object name\n> abbreviated to $2; otherwise we might be missing a /g in the sed\n> script.\n\nIn the v6 you rightly commented on the /g that was there previously not\nbeing needed :)\n\nSo I dropped it, in this case we can rely on only getting the\nabbreviated output.\n"},{"id":"446234","messageId":"xmqqh7a6cg17.fsf@gitster.g","threadId":"56641","inReplyTo":"220114.861r1att7j.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v7 1/6] object-name tests: add tests for ambiguous object blind spots","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-14T18:45:40Z","receivedAt":"2022-01-14T18:45:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Jan 13 2022, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> +test_cmp_failed_rev_parse () {\n>>> +\tcat >expect &&\n>>> +\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n>>> +\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n>>> +\ttest_cmp expect actual\n>>> +}\n>>\n>> That's dense, especially without a comment (or named variable) that\n>> hints readers what the arguments to this helper (and its standard\n>> input) ought to be.\n>\n> I got rid of the named variables from v6 in response to a \"shift\" that\n> shifted the wrong number, but perhaps I should have just removed the\n> \"shift\"?\n\nI agree that is a more sensible thing you could have done.\n\n>> As long as messages from rev-parse on the error stream never has\n>> more than one abbreviated object name on a single line, the above\n>> should give us a copy of the message with expected object name\n>> abbreviated to $2; otherwise we might be missing a /g in the sed\n>> script.\n>\n> In the v6 you rightly commented on the /g that was there previously not\n> being needed :)\n>\n> So I dropped it, in this case we can rely on only getting the\n> abbreviated output.\n\nI do not care either way, as long as it is clearly stated why /g is\nthere (or why /g is missing) for the future developers.\n"},{"id":"446237","messageId":"xmqq4k66cf50.fsf@gitster.g","threadId":"56641","inReplyTo":"220114.865yqmtt9z.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v7 4/6] object-name: show date for ambiguous tag objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-14T19:04:59Z","receivedAt":"2022-01-14T19:05:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> I still think the trade-off of not doing that discussed in the commit\n> message is better, i.e. (to quote upthread):\n>     \n>     We could detect that and emit a \"%s [bad tag object]\" message (to go\n>     with the existing generic \"%s [bad object]\"), but I don't think it's\n>     worth the effort. Users are unlikely to ever run into cases where\n>     they've got a broken object that's also ambiguous, and in case they do\n>     output that's a bit nonsensical beats wasting translator time on this\n>     obscure edge case.\n\nWriting the above (and quoting it again to make me respond to it)\nhave already wasted a lot more time than a better solution that does\nnot lead to a misleading output, especially given that it was given\nfor free to you already.\n"},{"id":"446239","messageId":"220114.86bl0ertvd.gmgdl@evledraar.gmail.com","threadId":"56641","inReplyTo":"xmqq4k66cf50.fsf@gitster.g","subject":"Re: [PATCH v7 4/6] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-14T19:35:00Z","receivedAt":"2022-01-14T19:37:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jan 14 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> I still think the trade-off of not doing that discussed in the commit\n>> message is better, i.e. (to quote upthread):\n>>     \n>>     We could detect that and emit a \"%s [bad tag object]\" message (to go\n>>     with the existing generic \"%s [bad object]\"), but I don't think it's\n>>     worth the effort. Users are unlikely to ever run into cases where\n>>     they've got a broken object that's also ambiguous, and in case they do\n>>     output that's a bit nonsensical beats wasting translator time on this\n>>     obscure edge case.\n>\n> Writing the above (and quoting it again to make me respond to it)\n> have already wasted a lot more time than a better solution that does\n> not lead to a misleading output, especially given that it was given\n> for free to you already.\n\nI don't mind changing it, but the reason I re-quoted it is because your\nreply seemed to suggest that you had skimmed past that part before\nmaking your original comment, not to merely repeat myself.\n\nI.e. it's basically suggesting \"how about?...\" without addressing the \"I\nintentionally didn't do this, because...\" argument in the commit\nmessage.\n\nBut sure, I'll add a translatable message for this edge case in a\nre-roll.\n"},{"id":"447039","messageId":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com","subject":"[PATCH v8 0/7] object-name: make ambiguous object output translatable + show tag date","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:42Z","receivedAt":"2022-01-27T05:26:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This topic improves the output we emit on ambiguous objects as noted\nin 5/7, and makes it translatable, see 4/7. See [1] for v7.\n\nThis v8 addresses feedback from Junio. There's a small test change +\ncommit message change in 1/7, and a rather small change in adding an\nexplicit message in 5/7 for tags that we cannot parse.\n\nThe range-diff looks rather scary though because if we're going to add\nsuch output it made sense to add it in a new 3/7, before we made the\noutput translatable, and then to carry that change forward.\n\n1. https://lore.kernel.org/git/cover-v7-0.6-00000000000-20220111T130811Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (7):\n  object-name tests: add tests for ambiguous object blind spots\n  object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n  object-name: explicitly handle bad tags in show_ambiguous_object()\n  object-name: make ambiguous object output translatable\n  object-name: show date for ambiguous tag objects\n  object-name: iterate ambiguous objects before showing header\n  object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n\n object-name.c                       | 121 +++++++++++++++++++++++++---\n t/t1512-rev-parse-disambiguation.sh |  81 +++++++++++++++++++\n 2 files changed, 190 insertions(+), 12 deletions(-)\n\nRange-diff against v7:\n1:  28c01b7f8a5 ! 1:  756c94bda7a object-name tests: add tests for ambiguous object blind spots\n    @@ Commit message\n         and because a subsequent commit will tweak it. Showing a diff of how\n         the output changes is helpful to explain those subsequent commits.\n     \n    +    The \"sed\" invocation in test_cmp_failed_rev_parse() doesn't need a\n    +    \"/g\" because under both SHA-1 and SHA-256 we'll wildcard match any\n    +    trailing part of the OID after our known starting prefix. We'd like to\n    +    convert all of that to just \"...\" for the \"test_cmp\" which follows.\n    +\n         1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    @@ t/t1512-rev-parse-disambiguation.sh: export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      . ./test-lib.sh\n      \n     +test_cmp_failed_rev_parse () {\n    ++\tdir=$1\n    ++\trev=$2\n    ++\n     +\tcat >expect &&\n    -+\ttest_must_fail git -C \"$1\" rev-parse \"$2\" 2>actual.raw &&\n    -+\tsed \"s/\\($2\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n    ++\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n    ++\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n     +\ttest_cmp expect actual\n     +}\n     +\n2:  b7027dfc843 = 2:  e60f100003a object-name: explicitly handle OBJ_BAD in show_ambiguous_object()\n4:  2e5511c9fa5 ! 3:  eaede34fa4f object-name: show date for ambiguous tag objects\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    object-name: show date for ambiguous tag objects\n    +    object-name: explicitly handle bad tags in show_ambiguous_object()\n     \n    -    Make the ambiguous tag object output nicer in the case of tag objects\n    -    such as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\n    -    the \"tagger\" header. I.e.:\n    +    Follow-up the handling of OBJ_BAD in the preceding commit and\n    +    explicitly handle those cases where parse_tag() fails, or we don't end\n    +    up with a non-NULL pointer in in tag->tag.\n     \n    -        $ git rev-parse b7e68\n    -        error: short object ID b7e68 is ambiguous\n    -        hint: The candidates are:\n    -        hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    -        hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    -        hint:   b7e68f6b413 tree\n    -        hint:   b7e68490b97 blob\n    -        b7e68\n    -        [...]\n    +    If we run into such a tag we'd previously be silent about it. We\n    +    really should also be handling these batter in parse_tag_buffer() by\n    +    being more eager to emit an error(), instead of silently aborting with\n    +    \"return -1;\".\n     \n    -    Before this we'd emit a \"tag\" line of:\n    +    One example of such a tag is the one that's tested for in\n    +    \"t3800-mktag.sh\", where the code takes the \"size <\n    +    the_hash_algo->hexsz + 24\" branch.\n     \n    -        hint:   b7e68c41d92 tag v2.32.0\n    +    But in lieu of earlier missing \"error\" output let's show the user\n    +    something to indicate why we're not showing a tag message in these\n    +    cases, now instead of showing:\n     \n    -    As with OBJ_COMMIT we punt on the cases where the date in the object\n    -    is nonsensical, and other cases where parse_tag() might fail. For\n    -    those we'll use our default date of \"0\" and tag message of\n    -    \"\". E.g. for some of the corrupt tags created by t3800-mktag.sh we'd\n    -    emit a line like:\n    +        hint:   deadbeef tag\n     \n    -        hint:   8d62cb0b06 tag 1970-01-01 -\n    +    We'll instead display:\n     \n    -    We could detect that and emit a \"%s [bad tag object]\" message (to go\n    -    with the existing generic \"%s [bad object]\"), but I don't think it's\n    -    worth the effort. Users are unlikely to ever run into cases where\n    -    they've got a broken object that's also ambiguous, and in case they do\n    -    output that's a bit nonsensical beats wasting translator time on this\n    -    obscure edge case.\n    -\n    -    We should instead change parse_tag_buffer() to be more eager to emit\n    -    an error() instead of silently aborting with \"return -1;\". In the case\n    -    of \"t3800-mktag.sh\" it takes the \"size < the_hash_algo->hexsz + 24\"\n    -    branch.\n    +        hint:   deadbeef tag [tag could not be parsed]\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## object-name.c ##\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - \t} else if (type == OBJ_TAG) {\n      \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n    - \t\tconst char *tag_tag = \"\";\n    -+\t\ttimestamp_t tag_date = 0;\n    - \n    --\t\tif (!parse_tag(tag) && tag->tag)\n    -+\t\tif (!parse_tag(tag) && tag->tag) {\n    - \t\t\ttag_tag = tag->tag;\n    -+\t\t\ttag_date = tag->date;\n    -+\t\t}\n    + \t\tif (!parse_tag(tag) && tag->tag)\n    + \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    ++\t\telse\n    ++\t\t\tstrbuf_addstr(&desc, \" [tag could not be parsed]\");\n    + \t}\n      \n    - \t\t/*\n    - \t\t * TRANSLATORS: This is a line of\n    - \t\t * ambiguous tag object output. E.g.:\n    - \t\t *\n    --\t\t *    \"deadbeef tag Some Tag Message\"\n    -+\t\t *    \"deadbeef tag 2021-01-01 - Some Tag Message\"\n    - \t\t *\n    - \t\t * The second argument is the \"tag\" string from\n    - \t\t * object.c.\n    - \t\t */\n    --\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n    -+\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n    -+\t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n    -+\t\t\t    tag_tag);\n    - \t} else if (type == OBJ_TREE) {\n    - \t\t/*\n    - \t\t * TRANSLATORS: This is a line of ambiguous <type>\n    + out:\n3:  65801f2c890 ! 4:  6a26c917a94 object-name: make ambiguous object output translatable\n    @@ Commit message\n         for RTL languages, who'd presumably like to change that to\n         \"%s<SP><SP>\\n\".\n     \n    +    In the case of the existing \"tag [tag could not be parsed]\" output\n    +    we'll now instead emit \"[bad tag, could not parse it]\". This is\n    +    consistent with the \"[bad object]\" output. Rephrasing the message like\n    +    this is possible because we're not unconditionally adding the\n    +    type_name() at the beginning.\n    +\n         1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n            2016-09-26)\n         2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n     +\t\tstrbuf_release(&msg);\n      \t} else if (type == OBJ_TAG) {\n      \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n    -+\t\tconst char *tag_tag = \"\";\n    -+\n    - \t\tif (!parse_tag(tag) && tag->tag)\n    +-\t\tif (!parse_tag(tag) && tag->tag)\n     -\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n    -+\t\t\ttag_tag = tag->tag;\n    +-\t\telse\n    +-\t\t\tstrbuf_addstr(&desc, \" [tag could not be parsed]\");\n     +\n    -+\t\t/*\n    -+\t\t * TRANSLATORS: This is a line of\n    -+\t\t * ambiguous tag object output. E.g.:\n    -+\t\t *\n    -+\t\t *    \"deadbeef tag Some Tag Message\"\n    -+\t\t *\n    -+\t\t * The second argument is the \"tag\" string from\n    -+\t\t * object.c.\n    -+\t\t */\n    -+\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag_tag);\n    ++\t\tif (!parse_tag(tag) && tag->tag) {\n    ++\t\t\t/*\n    ++\t\t\t * TRANSLATORS: This is a line of ambiguous\n    ++\t\t\t * tag object output. E.g.:\n    ++\t\t\t *\n    ++\t\t\t *    \"deadbeef tag Some Tag Message\"\n    ++\t\t\t *\n    ++\t\t\t * The second argument is the \"tag\" string\n    ++\t\t\t * from object.c.\n    ++\t\t\t */\n    ++\t\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag->tag);\n    ++\t\t} else {\n    ++\t\t\t/*\n    ++\t\t\t * TRANSLATORS: This is a line of ambiguous\n    ++\t\t\t * tag object output where we couldn't parse\n    ++\t\t\t * the tag itself. E.g.:\n    ++\t\t\t *\n    ++\t\t\t *    \"deadbeef tag [bad tag, could not parse it]\"\n    ++\t\t\t */\n    ++\t\t\tstrbuf_addf(&desc, _(\"%s [bad tag, could not parse it]\"),\n    ++\t\t\t\t    hash);\n    ++\t\t}\n     +\t} else if (type == OBJ_TREE) {\n     +\t\t/*\n     +\t\t * TRANSLATORS: This is a line of ambiguous <type>\n-:  ----------- > 5:  6237f07e3a9 object-name: show date for ambiguous tag objects\n5:  2c03cdd3c1e = 6:  57336c67dd2 object-name: iterate ambiguous objects before showing header\n6:  bf226f67099 ! 7:  1f0e1053918 object-name: re-use \"struct strbuf\" in show_ambiguous_object()\n    @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, voi\n      \t\tstrbuf_release(&date);\n      \t\tstrbuf_release(&msg);\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    - \t\t * The second argument is the \"tag\" string from\n    - \t\t * object.c.\n    - \t\t */\n    --\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n    -+\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n    - \t\t\t    show_date(tag_date, 0, DATE_MODE(SHORT)),\n    - \t\t\t    tag_tag);\n    + \t\t\t * The third argument is the \"tag\" string\n    + \t\t\t * from object.c.\n    + \t\t\t */\n    +-\t\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n    ++\t\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n    + \t\t\t\t    show_date(tag->date, 0, DATE_MODE(SHORT)),\n    + \t\t\t\t    tag->tag);\n    + \t\t} else {\n    +@@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n    + \t\t\t *\n    + \t\t\t *    \"deadbeef [bad tag, could not parse it]\"\n    + \t\t\t */\n    +-\t\t\tstrbuf_addf(&desc, _(\"%s [bad tag, could not parse it]\"),\n    ++\t\t\tstrbuf_addf(sb, _(\"%s [bad tag, could not parse it]\"),\n    + \t\t\t\t    hash);\n    + \t\t}\n      \t} else if (type == OBJ_TREE) {\n     @@ object-name.c: static int show_ambiguous_object(const struct object_id *oid, void *data)\n      \t\t * TRANSLATORS: This is a line of ambiguous <type>\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447038","messageId":"patch-v8-1.7-756c94bda7a-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 1/7] object-name tests: add tests for ambiguous object blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:43Z","receivedAt":"2022-01-27T05:26:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the tests for ambiguous objects to check how we handle objects\nwhere we return OBJ_BAD when trying to parse them. As noted in [1] we\nhave a blindspot when it comes to this behavior.\n\nSince we need to add new test data here let's extend these tests to be\ntested under SHA-256, in d7a2fc82491 (t1512: skip test if not using\nSHA-1, 2018-05-13) all of the existing tests were skipped, as they\nrely on specific SHA-1 object IDs.\n\nFor these tests it only matters that the first 4 characters of the OID\nprefix are the same for both SHA-1 and SHA-256. This uses strings that\nI mined, and have the same prefix when hashed with both.\n\nWe \"test_cmp\" the full output to guard against any future regressions,\nand because a subsequent commit will tweak it. Showing a diff of how\nthe output changes is helpful to explain those subsequent commits.\n\nThe \"sed\" invocation in test_cmp_failed_rev_parse() doesn't need a\n\"/g\" because under both SHA-1 and SHA-256 we'll wildcard match any\ntrailing part of the OID after our known starting prefix. We'd like to\nconvert all of that to just \"...\" for the \"test_cmp\" which follows.\n\n1. https://lore.kernel.org/git/YZwbphPpfGk78w2f@coredump.intra.peff.net/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1512-rev-parse-disambiguation.sh | 82 +++++++++++++++++++++++++++++\n 1 file changed, 82 insertions(+)\n\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex b0119bf8bc8..c14d88eae20 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -25,6 +25,88 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n \n+test_cmp_failed_rev_parse () {\n+\tdir=$1\n+\trev=$2\n+\n+\tcat >expect &&\n+\ttest_must_fail git -C \"$dir\" rev-parse \"$rev\" 2>actual.raw &&\n+\tsed \"s/\\($rev\\)[0-9a-f]*/\\1.../\" <actual.raw >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ambiguous blob output' '\n+\tgit init --bare blob.prefix &&\n+\t(\n+\t\tcd blob.prefix &&\n+\n+\t\t# Both start with \"dead..\", under both SHA-1 and SHA-256\n+\t\techo brocdnra | git hash-object -w --stdin &&\n+\t\techo brigddsv | git hash-object -w --stdin &&\n+\n+\t\t# Both start with \"beef..\"\n+\t\techo 1agllotbh | git hash-object -w --stdin &&\n+\t\techo 1bbfctrkc | git hash-object -w --stdin\n+\t) &&\n+\n+\ttest_must_fail git -C blob.prefix rev-parse dead &&\n+\ttest_cmp_failed_rev_parse blob.prefix beef <<-\\EOF\n+\terror: short object ID beef... is ambiguous\n+\thint: The candidates are:\n+\thint:   beef... blob\n+\thint:   beef... blob\n+\tfatal: ambiguous argument '\\''beef...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+'\n+\n+test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n+\tgit init --bare blob.bad &&\n+\t(\n+\t\tcd blob.bad &&\n+\n+\t\t# Both have the prefix \"bad0\"\n+\t\techo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&\n+\t\techo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally\n+\t) &&\n+\n+\ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n+\terror: short object ID bad0... is ambiguous\n+\thint: The candidates are:\n+\tfatal: invalid object type\n+\tEOF\n+'\n+\n+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n+\tgit init --bare blob.corrupt &&\n+\t(\n+\t\tcd blob.corrupt &&\n+\n+\t\t# Both have the prefix \"cafe\"\n+\t\techo bnkxmdwz | git hash-object -w --stdin &&\n+\t\toid=$(echo bmwsjxzi | git hash-object -w --stdin) &&\n+\n+\t\toidf=objects/$(test_oid_to_path \"$oid\") &&\n+\t\tchmod 755 $oidf &&\n+\t\techo broken >$oidf\n+\t) &&\n+\n+\ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n+\terror: short object ID cafe... is ambiguous\n+\thint: The candidates are:\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\terror: inflate: data stream error (incorrect header check)\n+\terror: unable to unpack cafe... header\n+\thint:   cafe... unknown type\n+\thint:   cafe... blob\n+\tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n+\tUse '\\''--'\\'' to separate paths from revisions, like this:\n+\t'\\''git <command> [<revision>...] -- [<file>...]'\\''\n+\tEOF\n+'\n+\n if ! test_have_prereq SHA1\n then\n \tskip_all='not using SHA-1 for objects'\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447040","messageId":"patch-v8-2.7-e60f100003a-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 2/7] object-name: explicitly handle OBJ_BAD in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:44Z","receivedAt":"2022-01-27T05:27:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the \"unknown type\" handling in the code that displays the\nambiguous object list to assert() that we're either going to get the\n\"real\" object types we can pass to type_name(), or a -1 (OBJ_BAD)\nreturn value from oid_object_info().\n\nSee [1] for the current output, and [1] for the commit that added the\n\"unknown type\" handling.\n\nWe are never going to get an \"unknown type\" in the sense of custom\ntypes crafted with \"hash-object --literally\", since we're not using\nthe OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag.\n\nIf we manage to otherwise unpack such an object without errors we'll\ndie() in parse_loose_header_extended() called by sort_ambiguous()\nbefore we get to show_ambiguous_object(), as is asserted by the test\nadded in the preceding commit.\n\nSo saying \"unknown type\" here was always misleading, we really meant\nto say that we had a failure parsing the object at all, i.e. that we\nhad repository corruption. If the problem is only that it's type is\nunknown we won't reach this code.\n\nSo let's emit a generic \"[bad object]\" instead. As our tests added in\nthe preceding commit show, we'll have emitted various \"error\" output\nalready in those cases.\n\nWe should do better in the truly \"unknown type\" cases, which we'd need\nto handle if we were passing down the OBJECT_INFO_ALLOW_UNKNOWN_TYPE\nflag. But let's leave that for some future improvement. In a\nsubsequent commit I'll improve the output we do show, and not having\nto handle the \"unknown type\" (as in OBJECT_INFO_ALLOW_UNKNOWN_TYPE)\nsimplifies that change.\n\n1. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n2. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 14 ++++++++++++--\n t/t1512-rev-parse-disambiguation.sh |  2 +-\n 2 files changed, 13 insertions(+), 3 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex fdff4601b2c..9750634ee76 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -361,6 +361,16 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\treturn 0;\n \n \ttype = oid_object_info(ds->repo, oid, NULL);\n+\n+\tif (type < 0) {\n+\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\tgoto out;\n+\t}\n+\n+\tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n+\t       type == OBJ_BLOB || type == OBJ_TAG);\n+\tstrbuf_addstr(&desc, type_name(type));\n+\n \tif (type == OBJ_COMMIT) {\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n \t\tif (commit) {\n@@ -374,9 +384,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n \t}\n \n-\tadvise(\"  %s %s%s\",\n+out:\n+\tadvise(\"  %s %s\",\n \t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       type_name(type) ? type_name(type) : \"unknown type\",\n \t       desc.buf);\n \n \tstrbuf_release(&desc);\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex c14d88eae20..80102cc43a3 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -99,7 +99,7 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n-\thint:   cafe... unknown type\n+\thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n \tUse '\\''--'\\'' to separate paths from revisions, like this:\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447041","messageId":"patch-v8-3.7-eaede34fa4f-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 3/7] object-name: explicitly handle bad tags in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:45Z","receivedAt":"2022-01-27T05:27:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up the handling of OBJ_BAD in the preceding commit and\nexplicitly handle those cases where parse_tag() fails, or we don't end\nup with a non-NULL pointer in in tag->tag.\n\nIf we run into such a tag we'd previously be silent about it. We\nreally should also be handling these batter in parse_tag_buffer() by\nbeing more eager to emit an error(), instead of silently aborting with\n\"return -1;\".\n\nOne example of such a tag is the one that's tested for in\n\"t3800-mktag.sh\", where the code takes the \"size <\nthe_hash_algo->hexsz + 24\" branch.\n\nBut in lieu of earlier missing \"error\" output let's show the user\nsomething to indicate why we're not showing a tag message in these\ncases, now instead of showing:\n\n    hint:   deadbeef tag\n\nWe'll instead display:\n\n    hint:   deadbeef tag [tag could not be parsed]\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/object-name.c b/object-name.c\nindex 9750634ee76..298b742bac9 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -382,6 +382,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n \t\tif (!parse_tag(tag) && tag->tag)\n \t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n+\t\telse\n+\t\t\tstrbuf_addstr(&desc, \" [tag could not be parsed]\");\n \t}\n \n out:\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447042","messageId":"patch-v8-4.7-6a26c917a94-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 4/7] object-name: make ambiguous object output translatable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:46Z","receivedAt":"2022-01-27T05:27:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the output of show_ambiguous_object() added in [1] and last\ntweaked in [2] and the preceding commit to be more friendly to\ntranslators.\n\nBy being able to customize the \"<SP><SP>%s\\n\" format we're even ready\nfor RTL languages, who'd presumably like to change that to\n\"%s<SP><SP>\\n\".\n\nIn the case of the existing \"tag [tag could not be parsed]\" output\nwe'll now instead emit \"[bad tag, could not parse it]\". This is\nconsistent with the \"[bad object]\" output. Rephrasing the message like\nthis is possible because we're not unconditionally adding the\ntype_name() at the beginning.\n\n1. 1ffa26c461 (get_short_sha1: list ambiguous objects on error,\n   2016-09-26)\n2. 5cc044e0257 (get_short_oid: sort ambiguous objects by type,\n   then SHA-1, 2018-05-10)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n object-name.c | 78 ++++++++++++++++++++++++++++++++++++++++++++-------\n 1 file changed, 68 insertions(+), 10 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 298b742bac9..f31b50bc315 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -356,40 +356,98 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tconst struct disambiguate_state *ds = data;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n+\tconst char *hash;\n \n \tif (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))\n \t\treturn 0;\n \n+\thash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);\n \ttype = oid_object_info(ds->repo, oid, NULL);\n \n \tif (type < 0) {\n-\t\tstrbuf_addstr(&desc, \"[bad object]\");\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous object\n+\t\t * output shown when we cannot look up or parse the\n+\t\t * object in question. E.g. \"deadbeef [bad object]\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n \tassert(type == OBJ_TREE || type == OBJ_COMMIT ||\n \t       type == OBJ_BLOB || type == OBJ_TAG);\n-\tstrbuf_addstr(&desc, type_name(type));\n \n \tif (type == OBJ_COMMIT) {\n+\t\tstruct strbuf date = STRBUF_INIT;\n+\t\tstruct strbuf msg = STRBUF_INIT;\n \t\tstruct commit *commit = lookup_commit(ds->repo, oid);\n+\n \t\tif (commit) {\n \t\t\tstruct pretty_print_context pp = {0};\n \t\t\tpp.date_mode.type = DATE_SHORT;\n-\t\t\tformat_commit_message(commit, \" %ad - %s\", &desc, &pp);\n+\t\t\tformat_commit_message(commit, \"%ad\", &date, &pp);\n+\t\t\tformat_commit_message(commit, \"%s\", &msg, &pp);\n \t\t}\n+\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous commit\n+\t\t * object output. E.g.:\n+\t\t *\n+\t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n+\t\t\t    hash, date.buf, msg.buf);\n+\n+\t\tstrbuf_release(&date);\n+\t\tstrbuf_release(&msg);\n \t} else if (type == OBJ_TAG) {\n \t\tstruct tag *tag = lookup_tag(ds->repo, oid);\n-\t\tif (!parse_tag(tag) && tag->tag)\n-\t\t\tstrbuf_addf(&desc, \" %s\", tag->tag);\n-\t\telse\n-\t\t\tstrbuf_addstr(&desc, \" [tag could not be parsed]\");\n+\n+\t\tif (!parse_tag(tag) && tag->tag) {\n+\t\t\t/*\n+\t\t\t * TRANSLATORS: This is a line of ambiguous\n+\t\t\t * tag object output. E.g.:\n+\t\t\t *\n+\t\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t\t *\n+\t\t\t * The second argument is the \"tag\" string\n+\t\t\t * from object.c.\n+\t\t\t */\n+\t\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag->tag);\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * TRANSLATORS: This is a line of ambiguous\n+\t\t\t * tag object output where we couldn't parse\n+\t\t\t * the tag itself. E.g.:\n+\t\t\t *\n+\t\t\t *    \"deadbeef tag [bad tag, could not parse it]\"\n+\t\t\t */\n+\t\t\tstrbuf_addf(&desc, _(\"%s [bad tag, could not parse it]\"),\n+\t\t\t\t    hash);\n+\t\t}\n+\t} else if (type == OBJ_TREE) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef tree\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t} else if (type == OBJ_BLOB) {\n+\t\t/*\n+\t\t * TRANSLATORS: This is a line of ambiguous <type>\n+\t\t * object output. E.g. \"deadbeef blob\".\n+\t\t */\n+\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n \t}\n \n+\n out:\n-\tadvise(\"  %s %s\",\n-\t       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),\n-\t       desc.buf);\n+\t/*\n+\t * TRANSLATORS: This is line item of ambiguous object output\n+\t * from describe_ambiguous_object() above. For RTL languages\n+\t * you'll probably want to swap the \"%s\" and leading \" \" space\n+\t * around.\n+\t */\n+\tadvise(_(\"  %s\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447043","messageId":"patch-v8-6.7-57336c67dd2-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 6/7] object-name: iterate ambiguous objects before showing header","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:48Z","receivedAt":"2022-01-27T05:27:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"The candidates are\" header that's shown for ambiguous\nobjects to be shown after we've iterated over all of the objects.\n\nIf we get any errors while doing so we don't want to split up the the\nheader and the list as a result. The two will now be printed together,\nas shown in the updated testcase.\n\nAs we're accumulating the lines into as \"struct strbuf\" before\nemitting them we need to add a trailing newline to the call in\nshow_ambiguous_object(). This and the change from \"The candidates\nare:\" to \"The candidates are:\\n%s\" helps to give translators more\ncontext.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c                       | 27 +++++++++++++++++++++++----\n t/t1512-rev-parse-disambiguation.sh |  3 +--\n 2 files changed, 24 insertions(+), 6 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex cbf459f5664..6154e1ec6f8 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -351,9 +351,16 @@ static int init_object_disambiguation(struct repository *r,\n \treturn 0;\n }\n \n+struct ambiguous_output {\n+\tconst struct disambiguate_state *ds;\n+\tstruct strbuf advice;\n+};\n+\n static int show_ambiguous_object(const struct object_id *oid, void *data)\n {\n-\tconst struct disambiguate_state *ds = data;\n+\tstruct ambiguous_output *state = data;\n+\tconst struct disambiguate_state *ds = state->ds;\n+\tstruct strbuf *advice = &state->advice;\n \tstruct strbuf desc = STRBUF_INIT;\n \tint type;\n \tconst char *hash;\n@@ -452,7 +459,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * you'll probably want to swap the \"%s\" and leading \" \" space\n \t * around.\n \t */\n-\tadvise(_(\"  %s\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n \n \tstrbuf_release(&desc);\n \treturn 0;\n@@ -551,6 +558,10 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \tif (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n+\t\tstruct ambiguous_output out = {\n+\t\t\t.ds = &ds,\n+\t\t\t.advice = STRBUF_INIT,\n+\t\t};\n \n \t\terror(_(\"short object ID %s is ambiguous\"), ds.hex_pfx);\n \n@@ -563,13 +574,21 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tif (!ds.ambiguous)\n \t\t\tds.fn = NULL;\n \n-\t\tadvise(_(\"The candidates are:\"));\n \t\trepo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);\n \t\tsort_ambiguous_oid_array(r, &collect);\n \n-\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &ds))\n+\t\tif (oid_array_for_each(&collect, show_ambiguous_object, &out))\n \t\t\tBUG(\"show_ambiguous_object shouldn't return non-zero\");\n+\n+\t\t/*\n+\t\t * TRANSLATORS: The argument is the list of ambiguous\n+\t\t * objects composed in show_ambiguous_object(). See\n+\t\t * its \"TRANSLATORS\" comments for details.\n+\t\t */\n+\t\tadvise(_(\"The candidates are:\\n%s\"), out.advice.buf);\n+\n \t\toid_array_clear(&collect);\n+\t\tstrbuf_release(&out.advice);\n \t}\n \n \treturn status;\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 80102cc43a3..98cefe3b703 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -73,7 +73,6 @@ test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '\n \n \ttest_cmp_failed_rev_parse blob.bad bad0 <<-\\EOF\n \terror: short object ID bad0... is ambiguous\n-\thint: The candidates are:\n \tfatal: invalid object type\n \tEOF\n '\n@@ -94,11 +93,11 @@ test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '\n \n \ttest_cmp_failed_rev_parse blob.corrupt cafe <<-\\EOF\n \terror: short object ID cafe... is ambiguous\n-\thint: The candidates are:\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n \terror: inflate: data stream error (incorrect header check)\n \terror: unable to unpack cafe... header\n+\thint: The candidates are:\n \thint:   cafe... [bad object]\n \thint:   cafe... blob\n \tfatal: ambiguous argument '\\''cafe...'\\'': unknown revision or path not in the working tree.\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447044","messageId":"patch-v8-5.7-6237f07e3a9-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 5/7] object-name: show date for ambiguous tag objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:47Z","receivedAt":"2022-01-27T05:27:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the ambiguous tag object output nicer in the case of tag objects\nsuch as ebf3c04b262 (Git 2.32, 2021-06-06) by including the date in\nthe \"tagger\" header. I.e.:\n\n    $ git rev-parse b7e68\n    error: short object ID b7e68 is ambiguous\n    hint: The candidates are:\n    hint:   b7e68c41d92 tag 2021-06-06 - v2.32.0\n    hint:   b7e68ae18e0 commit 2019-12-23 - bisect: use the standard 'if (!var)' way to check for 0\n    hint:   b7e68f6b413 tree\n    hint:   b7e68490b97 blob\n    b7e68\n    [...]\n\nBefore this we'd emit a \"tag\" line without a date, e.g.:\n\n    hint:   b7e68c41d92 tag v2.32.0\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex f31b50bc315..cbf459f5664 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -408,19 +408,24 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\t * TRANSLATORS: This is a line of ambiguous\n \t\t\t * tag object output. E.g.:\n \t\t\t *\n-\t\t\t *    \"deadbeef tag Some Tag Message\"\n+\t\t\t *    \"deadbeef tag 2022-01-01 - Some Tag Message\"\n \t\t\t *\n-\t\t\t * The second argument is the \"tag\" string\n+\t\t\t * The second argument is the YYYY-MM-DD found\n+\t\t\t * in the tag.\n+\t\t\t *\n+\t\t\t * The third argument is the \"tag\" string\n \t\t\t * from object.c.\n \t\t\t */\n-\t\t\tstrbuf_addf(&desc, _(\"%s tag %s\"), hash, tag->tag);\n+\t\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\t\t    show_date(tag->date, 0, DATE_MODE(SHORT)),\n+\t\t\t\t    tag->tag);\n \t\t} else {\n \t\t\t/*\n \t\t\t * TRANSLATORS: This is a line of ambiguous\n \t\t\t * tag object output where we couldn't parse\n \t\t\t * the tag itself. E.g.:\n \t\t\t *\n-\t\t\t *    \"deadbeef tag [bad tag, could not parse it]\"\n+\t\t\t *    \"deadbeef [bad tag, could not parse it]\"\n \t\t\t */\n \t\t\tstrbuf_addf(&desc, _(\"%s [bad tag, could not parse it]\"),\n \t\t\t\t    hash);\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447045","messageId":"patch-v8-7.7-1f0e1053918-20220127T052116Z-avarab@gmail.com","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"[PATCH v8 7/7] object-name: re-use \"struct strbuf\" in show_ambiguous_object()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-27T05:26:49Z","receivedAt":"2022-01-27T05:27:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Reduce the allocations done by show_ambiguous_object() by moving the\n\"desc\" strbuf into the \"struct ambiguous_output\" introduced in the\npreceding commit.\n\nThis doesn't matter for optimization purposes, but since we're\naccumulating a \"struct strbuf advice\" anyway let's follow that pattern\nand add a \"struct strbuf sb\", we can then strbuf_reset() it rather\nthan calling strbuf_release() for each call to\nshow_ambiguous_object().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n object-name.c | 23 +++++++++++++----------\n 1 file changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/object-name.c b/object-name.c\nindex 6154e1ec6f8..61b58a2f292 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -354,6 +354,7 @@ static int init_object_disambiguation(struct repository *r,\n struct ambiguous_output {\n \tconst struct disambiguate_state *ds;\n \tstruct strbuf advice;\n+\tstruct strbuf sb;\n };\n \n static int show_ambiguous_object(const struct object_id *oid, void *data)\n@@ -361,7 +362,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \tstruct ambiguous_output *state = data;\n \tconst struct disambiguate_state *ds = state->ds;\n \tstruct strbuf *advice = &state->advice;\n-\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct strbuf *sb = &state->sb;\n \tint type;\n \tconst char *hash;\n \n@@ -377,7 +378,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * output shown when we cannot look up or parse the\n \t\t * object in question. E.g. \"deadbeef [bad object]\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s [bad object]\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s [bad object]\"), hash);\n \t\tgoto out;\n \t}\n \n@@ -402,8 +403,8 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t *\n \t\t *    \"deadbeef commit 2021-01-01 - Some Commit Message\"\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s commit %s - %s\"),\n-\t\t\t    hash, date.buf, msg.buf);\n+\t\tstrbuf_addf(sb, _(\"%s commit %s - %s\"), hash, date.buf,\n+\t\t\t    msg.buf);\n \n \t\tstrbuf_release(&date);\n \t\tstrbuf_release(&msg);\n@@ -423,7 +424,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\t * The third argument is the \"tag\" string\n \t\t\t * from object.c.\n \t\t\t */\n-\t\t\tstrbuf_addf(&desc, _(\"%s tag %s - %s\"), hash,\n+\t\t\tstrbuf_addf(sb, _(\"%s tag %s - %s\"), hash,\n \t\t\t\t    show_date(tag->date, 0, DATE_MODE(SHORT)),\n \t\t\t\t    tag->tag);\n \t\t} else {\n@@ -434,7 +435,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t\t *\n \t\t\t *    \"deadbeef [bad tag, could not parse it]\"\n \t\t\t */\n-\t\t\tstrbuf_addf(&desc, _(\"%s [bad tag, could not parse it]\"),\n+\t\t\tstrbuf_addf(sb, _(\"%s [bad tag, could not parse it]\"),\n \t\t\t\t    hash);\n \t\t}\n \t} else if (type == OBJ_TREE) {\n@@ -442,13 +443,13 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef tree\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s tree\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s tree\"), hash);\n \t} else if (type == OBJ_BLOB) {\n \t\t/*\n \t\t * TRANSLATORS: This is a line of ambiguous <type>\n \t\t * object output. E.g. \"deadbeef blob\".\n \t\t */\n-\t\tstrbuf_addf(&desc, _(\"%s blob\"), hash);\n+\t\tstrbuf_addf(sb, _(\"%s blob\"), hash);\n \t}\n \n \n@@ -459,9 +460,9 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)\n \t * you'll probably want to swap the \"%s\" and leading \" \" space\n \t * around.\n \t */\n-\tstrbuf_addf(advice, _(\"  %s\\n\"), desc.buf);\n+\tstrbuf_addf(advice, _(\"  %s\\n\"), sb->buf);\n \n-\tstrbuf_release(&desc);\n+\tstrbuf_reset(sb);\n \treturn 0;\n }\n \n@@ -560,6 +561,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \t\tstruct oid_array collect = OID_ARRAY_INIT;\n \t\tstruct ambiguous_output out = {\n \t\t\t.ds = &ds,\n+\t\t\t.sb = STRBUF_INIT,\n \t\t\t.advice = STRBUF_INIT,\n \t\t};\n \n@@ -589,6 +591,7 @@ static enum get_oid_result get_short_oid(struct repository *r,\n \n \t\toid_array_clear(&collect);\n \t\tstrbuf_release(&out.advice);\n+\t\tstrbuf_release(&out.sb);\n \t}\n \n \treturn status;\n-- \n2.35.0.890.gd7e422415d9\n\n"},{"id":"447125","messageId":"xmqqee4t0wdw.fsf@gitster.g","threadId":"56641","inReplyTo":"cover-v8-0.7-00000000000-20220127T052116Z-avarab@gmail.com","subject":"Re: [PATCH v8 0/7] object-name: make ambiguous object output translatable + show tag date","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-27T20:14:03Z","receivedAt":"2022-01-27T20:14:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The range-diff looks rather scary though because if we're going to add\n> such output it made sense to add it in a new 3/7, before we made the\n> output translatable, and then to carry that change forward.\n\nI agree with the direction.  \"translatable plus show date\" are two\nunrelated things, but it is easier to understand if the feature\nenhancement is done first, so that marking and restructuring of the\nmessages for i18n needs to be done only once.\n\nWill queue.  Thanks.\n"}]}