On Thu, Sep 06 2018, Jeff King wrote:
> On Thu, Sep 06, 2018 at 04:01:39PM +0200, Ævar Arnfjörð Bjarmason wrote:
>
>> If we don't have some test for these sort of aliasing loops that fails
>> now, we really should add that in a 1/2 and fix it in this patch in 2/2.
>
> Yes, I'd agree that this is worth adding a test (especially if the
> output routines get more complex).
>
>> That makes sense from an implementaion perspective, i.e. we lookup "bar"
>> twice. But let's do better. If I have aliase like:
>>
>> a = b
>> b = c
>> c = d
>> d = e
>> e = c
>>
>> It should be telling me that my "e" expansion looped back to the "c = d"
>> expansion. Here's a patch to implement that, feel free to either squash
>> it in with my Signed-Off-By, or tacked onto a v4 version of this,
>> whichever you think makes sense:
>
> I don't have a strong opinion on whether this is worth it, but I think
> your implementation could be a little simpler:
>
>> diff --git a/git.c b/git.c
>> index 64f5fbd572..38f1033e52 100644
>> --- a/git.c
>> +++ b/git.c
>> @@ -692,8 +692,64 @@ static int run_argv(int *argcp, const char ***argv)
>> /* .. then try the external ones */
>> execv_dashed_external(*argv);
>>
>> - if (string_list_has_string(&cmd_list, *argv[0]))
>> - die(_("loop alias: %s is called twice"), *argv[0]);
>> + if (string_list_has_string(&cmd_list, *argv[0])) {
>> + struct strbuf sb = STRBUF_INIT;
>> + int i, seen_at_idx = -1;
>> +
>> + /*
>> + * Find the re-entry point for the alias
>> + * loop. TODO: There really should be a
>> + * "return the index of the first matching"
>> + * helper in string-list.c.
>> + */
>> + for (i = 0; i < cmd_list.nr; i++) {
>> + if (!strcmp(*argv[0], cmd_list.items[i].string))
>> + seen_at_idx = i;
>> + }
>> + assert(seen_at_idx != -1);
>
> The string-list code doesn't generally deal in indices. You can use
> string_list_find_insert_index(), but its return value is a little funky
> for the existing case. You can also just do:
>
> struct string_list_item *seen;
> ...
> seen = string_list_lookup(&cmd_list, *argv[0]);
> if (seen) {
> /* we have a loop */
> int idx = seen - cmd_list.items;
>
> That's a little intimate with the string-list implementation as an array
> of string_list, but it's already pretty standard to walk over and
> dereference that list (including in your patch). But also see below.
>
> Side note: there's actually a bigger problem with the original patch:
> the string list is unsorted (because it uses string_list_append(), and
> which is why your linear walk works here). But string_list_has_string()
> assumes it is sorted. So I think we'd actually want to use
> unsorted_string_list_has_string() or unsorted_string_list_lookup().
>
>> + for (i = 1; i < cmd_list.nr; i++) {
>> + if (i - 1 == seen_at_idx)
>> + /*
>> + * TRANSLATORS: This is a the
>> + * re-enttry point in the list
>> + * printed out by the "alias
>> + * loop" message below.
>> + */
>> + strbuf_addf(&sb, _(" %d. %s = %s <==
>> The re-entry point in the loop\n"),
>> + i,
>> + cmd_list.items[i -
>> 1].string,
>> + cmd_list.items[i].string);
>
> This is always going to show the right-hand of the equals as the
> left-hand on the next line. Would it be simpler to just show the list?
> Likewise, the last item in the list is always going to be "where the
> loop started". Do we need to say that?
Yeah maybe that's overzealous. I figured in the spirit of clang & GCC
compiler messages these days there's no such thing as too dumbed down :)
> E.g., something like:
>
> seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
> if (seen) {
> for (i = 0; i < cmd_list.nr; i++) {
> struct string_list *item = cmd_list.items[i];
>
> strbuf_addf(&sb, " %s", item->string);
> if (item == seen)
> strbuf_add(&sb, " <==");
> strbuf_addch(&sb, '\n');
> }
> /* We never added this to the list, but we were about to */
> strbuf_addch(" %s\n", seen->string);
> die(...);
> }
>
> I guess it's not that far off of yours. Not using words to describe the
> loop entry and exit points avoids translation, which avoids notes to
> translators, which is most of what makes your patch long. ;)
This still needs translation for RTL languages. I.e. they'd want to
print out the equivalent of "%s " followed by "==> %s ". We happen to
(unfortunately) not carry such a language yet, but it's worth
future-proofing output as we add it in case we get one.