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.