run-command.c: print env vars in trace_run_command()
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Thu, 18 Jan 2018 09:45:11 +0000 (16:45 +0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Jan 2018 18:49:20 +0000 (10:49 -0800)
Occasionally submodule code could execute new commands with GIT_DIR set
to some submodule. GIT_TRACE prints just the command line which makes it
hard to tell that it's not really executed on this repository.

Print the env delta (compared to parent environment) in this case.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
run-command.c
t/helper/test-run-command.c
t/t0061-run-command.sh
index 326c33e3cd4e42e4d9eb898e579701b307e4cfa4..1301b878c7eece7031fb0c9795195435a89ce7a4 100644 (file)
@@ -557,6 +557,63 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
        return code;
 }
 
+static void trace_add_env(struct strbuf *dst, const char *const *deltaenv)
+{
+       struct string_list envs = STRING_LIST_INIT_DUP;
+       const char *const *e;
+       int i;
+       int printed_unset = 0;
+
+       /* Last one wins, see run-command.c:prep_childenv() for context */
+       for (e = deltaenv; e && *e; e++) {
+               struct strbuf key = STRBUF_INIT;
+               char *equals = strchr(*e, '=');
+
+               if (equals) {
+                       strbuf_add(&key, *e, equals - *e);
+                       string_list_insert(&envs, key.buf)->util = equals + 1;
+               } else {
+                       string_list_insert(&envs, *e)->util = NULL;
+               }
+               strbuf_release(&key);
+       }
+
+       /* "unset X Y...;" */
+       for (i = 0; i < envs.nr; i++) {
+               const char *var = envs.items[i].string;
+               const char *val = envs.items[i].util;
+
+               if (val || !getenv(var))
+                       continue;
+
+               if (!printed_unset) {
+                       strbuf_addstr(dst, " unset");
+                       printed_unset = 1;
+               }
+               strbuf_addf(dst, " %s", var);
+       }
+       if (printed_unset)
+               strbuf_addch(dst, ';');
+
+       /* ... followed by "A=B C=D ..." */
+       for (i = 0; i < envs.nr; i++) {
+               const char *var = envs.items[i].string;
+               const char *val = envs.items[i].util;
+               const char *oldval;
+
+               if (!val)
+                       continue;
+
+               oldval = getenv(var);
+               if (oldval && !strcmp(val, oldval))
+                       continue;
+
+               strbuf_addf(dst, " %s=", var);
+               sq_quote_buf_pretty(dst, val);
+       }
+       string_list_clear(&envs, 0);
+}
+
 static void trace_run_command(const struct child_process *cp)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -565,6 +622,12 @@ static void trace_run_command(const struct child_process *cp)
                return;
 
        strbuf_addf(&buf, "trace: run_command:");
+       /*
+        * The caller is responsible for initializing cp->env from
+        * cp->env_array if needed. We only check one place.
+        */
+       if (cp->env)
+               trace_add_env(&buf, cp->env);
        if (cp->git_cmd)
                strbuf_addstr(&buf, " git");
        sq_quote_argv_pretty(&buf, cp->argv);
index d24d157379f30cafdeeb772e82bf5ee4c862272c..153342e44dd11ae357cc299a9214f4c365614a5e 100644 (file)
@@ -54,6 +54,15 @@ int cmd_main(int argc, const char **argv)
        struct child_process proc = CHILD_PROCESS_INIT;
        int jobs;
 
+       if (argc < 3)
+               return 1;
+       while (!strcmp(argv[1], "env")) {
+               if (!argv[2])
+                       die("env specifier without a value");
+               argv_array_push(&proc.env_array, argv[2]);
+               argv += 2;
+               argc -= 2;
+       }
        if (argc < 3)
                return 1;
        proc.argv = (const char **)argv + 2;
index e4739170aa2b7c833cd51a06a7ac6764c8bf0494..24c92b6cd7b1c54eb6541a81abd7e5812b3b99b0 100755 (executable)
@@ -141,4 +141,41 @@ test_expect_success 'run_command outputs ' '
        test_cmp expect actual
 '
 
+test_trace () {
+       expect="$1"
+       shift
+       GIT_TRACE=1 test-run-command "$@" run-command true 2>&1 >/dev/null | \
+               sed 's/.* run_command: //' >actual &&
+       echo "$expect true" >expect &&
+       test_cmp expect actual
+}
+
+test_expect_success 'GIT_TRACE with environment variables' '
+       test_trace "abc=1 def=2" env abc=1 env def=2 &&
+       test_trace "abc=2" env abc env abc=1 env abc=2 &&
+       test_trace "abc=2" env abc env abc=2 &&
+       (
+               abc=1 && export abc &&
+               test_trace "def=1" env abc=1 env def=1
+       ) &&
+       (
+               abc=1 && export abc &&
+               test_trace "def=1" env abc env abc=1 env def=1
+       ) &&
+       test_trace "def=1" env non-exist env def=1 &&
+       test_trace "abc=2" env abc=1 env abc env abc=2 &&
+       (
+               abc=1 def=2 && export abc def &&
+               test_trace "unset abc def;" env abc env def
+       ) &&
+       (
+               abc=1 def=2 && export abc def &&
+               test_trace "unset def; abc=3" env abc env def env abc=3
+       ) &&
+       (
+               abc=1 && export abc &&
+               test_trace "unset abc;" env abc=2 env abc
+       )
+'
+
 test_done