alias: use the early config machinery to expand aliases

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>

Johannes Schindelin committed Jun 14, 2017 at 13:36 UTC a9bcf6586d1a4888aea91553d73cda20494b8335
3 files changed +26 -59
alias.c
+21 -7
@@ -1,14 +1,28 @@
1 #include "cache.h"
2
3 +struct config_alias_data {
4 + const char *alias;
5 + char *v;
6 +};
7 +
8 +static int config_alias_cb(const char *key, const char *value, void *d)
9 +{
10 + struct config_alias_data *data = d;
11 + const char *p;
12 +
13 + if (skip_prefix(key, "alias.", &p) && !strcmp(p, data->alias))
14 + return git_config_string((const char **)&data->v, key, value);
15 +
16 + return 0;
17 +}
18 +
19 char *alias_lookup(const char *alias)
20 {
5 - char *v = NULL;
6 - struct strbuf key = STRBUF_INIT;
7 - strbuf_addf(&key, "alias.%s", alias);
8 - if (git_config_key_is_valid(key.buf))
9 - git_config_get_string(key.buf, &v);
10 - strbuf_release(&key);
11 - return v;
21 + struct config_alias_data data = { alias, NULL };
22 +
23 + read_early_config(config_alias_cb, &data);
24 +
25 + return data.v;
26 }
27
28 #define SPLIT_CMDLINE_BAD_ENDING 1
git.c
+4 -51
@@ -16,50 +16,6 @@ const char git_more_info_string[] =
16 "to read about a specific subcommand or concept.");
17
18 static int use_pager = -1;
19 -static char *orig_cwd;
20 -static const char *env_names[] = {
21 - GIT_DIR_ENVIRONMENT,
22 - GIT_WORK_TREE_ENVIRONMENT,
23 - GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
24 - GIT_PREFIX_ENVIRONMENT
25 -};
26 -static char *orig_env[4];
27 -static int save_restore_env_balance;
28 -
29 -static void save_env_before_alias(void)
30 -{
31 - int i;
32 -
33 - assert(save_restore_env_balance == 0);
34 - save_restore_env_balance = 1;
35 - orig_cwd = xgetcwd();
36 - for (i = 0; i < ARRAY_SIZE(env_names); i++) {
37 - orig_env[i] = getenv(env_names[i]);
38 - orig_env[i] = xstrdup_or_null(orig_env[i]);
39 - }
40 -}
41 -
42 -static void restore_env(int external_alias)
43 -{
44 - int i;
45 -
46 - assert(save_restore_env_balance == 1);
47 - save_restore_env_balance = 0;
48 - if (!external_alias && orig_cwd && chdir(orig_cwd))
49 - die_errno("could not move to %s", orig_cwd);
50 - free(orig_cwd);
51 - for (i = 0; i < ARRAY_SIZE(env_names); i++) {
52 - if (external_alias &&
53 - !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
54 - continue;
55 - if (orig_env[i]) {
56 - setenv(env_names[i], orig_env[i], 1);
57 - free(orig_env[i]);
58 - } else {
59 - unsetenv(env_names[i]);
60 - }
61 - }
62 -}
19
20 static void commit_pager_choice(void) {
21 switch (use_pager) {
@@ -250,19 +206,18 @@ static int handle_alias(int *argcp, const char ***argv)
206 const char **new_argv;
207 const char *alias_command;
208 char *alias_string;
253 - int unused_nongit;
254 -
255 - save_env_before_alias();
256 - setup_git_directory_gently(&unused_nongit);
209
210 alias_command = (*argv)[0];
211 alias_string = alias_lookup(alias_command);
212 if (alias_string) {
213 if (alias_string[0] == '!') {
214 struct child_process child = CHILD_PROCESS_INIT;
215 + int nongit_ok;
216 +
217 + /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
218 + setup_git_directory_gently(&nongit_ok);
219
220 commit_pager_choice();
265 - restore_env(1);
221
222 child.use_shell = 1;
223 argv_array_push(&child.args, alias_string + 1);
@@ -308,8 +263,6 @@ static int handle_alias(int *argcp, const char ***argv)
263 ret = 1;
264 }
265
311 - restore_env(0);
312 -
266 errno = saved_errno;
267
268 return ret;
t/t7006-pager.sh
+1 -1
@@ -391,7 +391,7 @@ test_expect_success TTY 'core.pager in repo config works and retains cwd' '
391 )
392 '
393
394 -test_expect_failure TTY 'core.pager is found via alias in subdirectory' '
394 +test_expect_success TTY 'core.pager is found via alias in subdirectory' '
395 sane_unset GIT_PAGER &&
396 test_config core.pager "cat >via-alias" &&
397 (