diff --git a/git.c b/git.c index da278c3d4..6c64c9430 100644 --- a/git.c +++ b/git.c @@ -25,14 +25,14 @@ static const char *env_names[] = { GIT_PREFIX_ENVIRONMENT }; static char *orig_env[4]; -static int saved_env_before_alias; +static int save_restore_env_balance; static void save_env_before_alias(void) { int i; - if (saved_env_before_alias) - return; - saved_env_before_alias = 1; + + assert(save_restore_env_balance == 0); + save_restore_env_balance = 1; orig_cwd = xgetcwd(); for (i = 0; i < ARRAY_SIZE(env_names); i++) { orig_env[i] = getenv(env_names[i]); @@ -44,6 +44,9 @@ static void save_env_before_alias(void) static void restore_env(int external_alias) { int i; + + assert(save_restore_env_balance == 1); + save_restore_env_balance = 0; if (!external_alias && orig_cwd && chdir(orig_cwd)) die_errno("could not move to %s", orig_cwd); free(orig_cwd); @@ -51,10 +54,12 @@ static void restore_env(int external_alias) if (external_alias && !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT)) continue; - if (orig_env[i]) + if (orig_env[i]) { setenv(env_names[i], orig_env[i], 1); - else + free(orig_env[i]); + } else { unsetenv(env_names[i]); + } } } @@ -531,16 +536,8 @@ static void handle_builtin(int argc, const char **argv) } builtin = get_builtin(cmd); - if (builtin) { - /* - * XXX: if we can figure out cases where it is _safe_ - * to do, we can avoid spawning a new process when - * saved_env_before_alias is true - * (i.e. setup_git_dir* has been run once) - */ - if (!saved_env_before_alias) - exit(run_builtin(builtin, argc, argv)); - } + if (builtin) + exit(run_builtin(builtin, argc, argv)); } static void execv_dashed_external(const char **argv) @@ -584,8 +581,17 @@ static int run_argv(int *argcp, const char ***argv) int done_alias = 0; while (1) { - /* See if it's a builtin */ - handle_builtin(*argcp, *argv); + /* + * If we tried alias and futzed with our environment, + * it no longer is safe to invoke builtins directly in + * general. We have to spawn them as dashed externals. + * + * NEEDSWORK: if we can figure out cases + * where it is safe to do, we can avoid spawning a new + * process. + */ + if (!done_alias) + handle_builtin(*argcp, *argv); /* .. then try the external ones */ execv_dashed_external(*argv); diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh new file mode 100755 index 000000000..dfe95c923 --- /dev/null +++ b/t/t9003-help-autocorrect.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='help.autocorrect finding a match' +. ./test-lib.sh + +test_expect_success 'setup' ' + # An alias + git config alias.lgf "log --format=%s --first-parent" && + + # A random user-defined command + write_script git-distimdistim <<-EOF && + echo distimdistim was called + EOF + + PATH="$PATH:." && + export PATH && + + git commit --allow-empty -m "a single log entry" && + + # Sanity check + git lgf >actual && + echo "a single log entry" >expect && + test_cmp expect actual && + + git distimdistim >actual && + echo "distimdistim was called" >expect && + test_cmp expect actual +' + +test_expect_success 'autocorrect showing candidates' ' + git config help.autocorrect 0 && + + test_must_fail git lfg 2>actual && + sed -e "1,/^Did you mean this/d" actual | grep lgf && + + test_must_fail git distimdist 2>actual && + sed -e "1,/^Did you mean this/d" actual | grep distimdistim +' + +test_expect_success 'autocorrect running commands' ' + git config help.autocorrect -1 && + + git lfg >actual && + echo "a single log entry" >expect && + test_cmp expect actual && + + git distimdist >actual && + echo "distimdistim was called" >expect && + test_cmp expect actual +' + +test_done