alias: use the early config machinery to expand aliases
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 14 Jun 2017 11:36:00 +0000 (13:36 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Jun 2017 19:31:50 +0000 (12:31 -0700)
Instead of discovering the .git/ directory, reading the config and then
trying to painstakingly reset all the global state if we did not find a
matching alias, let's use the early config machinery instead.

It may look like unnecessary work to discover the .git/ directory in the
early config machinery and then call setup_git_directory_gently() in the
case of a shell alias, repeating the very same discovery *again*.
However, we have to do this as the early config machinery takes pains
*not* to touch any global state, while shell aliases expect a possibly
changed working directory and at least the GIT_PREFIX and GIT_DIR
variables to be set.

This change also fixes a known issue where Git tried to read the pager
config from an incorrect path in a subdirectory of a Git worktree if an
alias expanded to a shell command.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
alias.c
git.c
t/t7006-pager.sh
diff --git a/alias.c b/alias.c
index 3b90397a99d9f7ed4a0c1c5a83f5e69c879e752f..05263046614e10945cb8d81ef3e42e0d5f2c9a2e 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -1,14 +1,28 @@
 #include "cache.h"
 
+struct config_alias_data {
+       const char *alias;
+       char *v;
+};
+
+static int config_alias_cb(const char *key, const char *value, void *d)
+{
+       struct config_alias_data *data = d;
+       const char *p;
+
+       if (skip_prefix(key, "alias.", &p) && !strcmp(p, data->alias))
+               return git_config_string((const char **)&data->v, key, value);
+
+       return 0;
+}
+
 char *alias_lookup(const char *alias)
 {
-       char *v = NULL;
-       struct strbuf key = STRBUF_INIT;
-       strbuf_addf(&key, "alias.%s", alias);
-       if (git_config_key_is_valid(key.buf))
-               git_config_get_string(key.buf, &v);
-       strbuf_release(&key);
-       return v;
+       struct config_alias_data data = { alias, NULL };
+
+       read_early_config(config_alias_cb, &data);
+
+       return data.v;
 }
 
 #define SPLIT_CMDLINE_BAD_ENDING 1
diff --git a/git.c b/git.c
index 8ff44f081d43176474b267de5451f2c2e88089d0..58ef570294da1ffc8fed38609d140833426dd14f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -16,50 +16,6 @@ const char git_more_info_string[] =
           "to read about a specific subcommand or concept.");
 
 static int use_pager = -1;
-static char *orig_cwd;
-static const char *env_names[] = {
-       GIT_DIR_ENVIRONMENT,
-       GIT_WORK_TREE_ENVIRONMENT,
-       GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
-       GIT_PREFIX_ENVIRONMENT
-};
-static char *orig_env[4];
-static int save_restore_env_balance;
-
-static void save_env_before_alias(void)
-{
-       int i;
-
-       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]);
-               orig_env[i] = xstrdup_or_null(orig_env[i]);
-       }
-}
-
-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);
-       for (i = 0; i < ARRAY_SIZE(env_names); i++) {
-               if (external_alias &&
-                   !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
-                       continue;
-               if (orig_env[i]) {
-                       setenv(env_names[i], orig_env[i], 1);
-                       free(orig_env[i]);
-               } else {
-                       unsetenv(env_names[i]);
-               }
-       }
-}
 
 static void commit_pager_choice(void) {
        switch (use_pager) {
@@ -250,19 +206,18 @@ static int handle_alias(int *argcp, const char ***argv)
        const char **new_argv;
        const char *alias_command;
        char *alias_string;
-       int unused_nongit;
-
-       save_env_before_alias();
-       setup_git_directory_gently(&unused_nongit);
 
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
                        struct child_process child = CHILD_PROCESS_INIT;
+                       int nongit_ok;
+
+                       /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
+                       setup_git_directory_gently(&nongit_ok);
 
                        commit_pager_choice();
-                       restore_env(1);
 
                        child.use_shell = 1;
                        argv_array_push(&child.args, alias_string + 1);
@@ -308,8 +263,6 @@ static int handle_alias(int *argcp, const char ***argv)
                ret = 1;
        }
 
-       restore_env(0);
-
        errno = saved_errno;
 
        return ret;
index 83881ec3a0c04e80911f14ca3307900332b2ac41..20b4d83c281e2fde8812f309bdebef012c6ecb74 100755 (executable)
@@ -391,7 +391,7 @@ test_expect_success TTY 'core.pager in repo config works and retains cwd' '
        )
 '
 
-test_expect_failure TTY 'core.pager is found via alias in subdirectory' '
+test_expect_success TTY 'core.pager is found via alias in subdirectory' '
        sane_unset GIT_PAGER &&
        test_config core.pager "cat >via-alias" &&
        (