diff --git a/git.c b/git.c index a6f4b44af5..5920f8019b 100644 --- a/git.c +++ b/git.c @@ -675,6 +675,8 @@ static void execv_dashed_external(const char **argv) static int run_argv(int *argcp, const char ***argv) { int done_alias = 0; + struct string_list cmd_list = STRING_LIST_INIT_NODUP; + struct string_list_item *seen; while (1) { /* @@ -692,17 +694,37 @@ static int run_argv(int *argcp, const char ***argv) /* .. then try the external ones */ execv_dashed_external(*argv); - /* It could be an alias -- this works around the insanity + seen = unsorted_string_list_lookup(&cmd_list, *argv[0]); + if (seen) { + int i; + struct strbuf sb = STRBUF_INIT; + for (i = 0; i < cmd_list.nr; i++) { + struct string_list_item *item = &cmd_list.items[i]; + + strbuf_addf(&sb, "\n %s", item->string); + if (item == seen) + strbuf_addstr(&sb, " <=="); + else if (i == cmd_list.nr - 1) + strbuf_addstr(&sb, " ==>"); + } + die(_("alias loop detected: expansion of '%s' does" + " not terminate:%s"), cmd_list.items[0].string, sb.buf); + } + + string_list_append(&cmd_list, *argv[0]); + + /* + * It could be an alias -- this works around the insanity * of overriding "git log" with "git show" by having * alias.log = show */ - if (done_alias) - break; if (!handle_alias(argcp, argv)) break; done_alias = 1; } + string_list_clear(&cmd_list, 0); + return done_alias; } diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh new file mode 100755 index 0000000000..a070e645d7 --- /dev/null +++ b/t/t0014-alias.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='git command aliasing' + +. ./test-lib.sh + +test_expect_success 'nested aliases - internal execution' ' + git config alias.nested-internal-1 nested-internal-2 && + git config alias.nested-internal-2 status && + git nested-internal-1 >output && + test_i18ngrep "^On branch " output +' + +test_expect_success 'nested aliases - mixed execution' ' + git config alias.nested-external-1 nested-external-2 && + git config alias.nested-external-2 "!git nested-external-3" && + git config alias.nested-external-3 status && + git nested-external-1 >output && + test_i18ngrep "^On branch " output +' + +test_expect_success 'looping aliases - internal execution' ' + git config alias.loop-internal-1 loop-internal-2 && + git config alias.loop-internal-2 loop-internal-3 && + git config alias.loop-internal-3 loop-internal-2 && + test_must_fail git loop-internal-1 2>output && + test_i18ngrep "^fatal: alias loop detected: expansion of" output +' + +# This test is disabled until external loops are fixed, because would block +# the test suite for a full minute. +# +#test_expect_failure 'looping aliases - mixed execution' ' +# git config alias.loop-mixed-1 loop-mixed-2 && +# git config alias.loop-mixed-2 "!git loop-mixed-1" && +# test_must_fail git loop-mixed-1 2>output && +# test_i18ngrep "^fatal: alias loop detected: expansion of" output +#' + +test_done