Skip to content

Commit c61a975

Browse files
pcloudsgitster
authored andcommitted
run-command.c: print env vars in trace_run_command()
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 <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 21dfc5e commit c61a975

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

run-command.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,63 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
557557
return code;
558558
}
559559

560+
static void trace_add_env(struct strbuf *dst, const char *const *deltaenv)
561+
{
562+
struct string_list envs = STRING_LIST_INIT_DUP;
563+
const char *const *e;
564+
int i;
565+
int printed_unset = 0;
566+
567+
/* Last one wins, see run-command.c:prep_childenv() for context */
568+
for (e = deltaenv; e && *e; e++) {
569+
struct strbuf key = STRBUF_INIT;
570+
char *equals = strchr(*e, '=');
571+
572+
if (equals) {
573+
strbuf_add(&key, *e, equals - *e);
574+
string_list_insert(&envs, key.buf)->util = equals + 1;
575+
} else {
576+
string_list_insert(&envs, *e)->util = NULL;
577+
}
578+
strbuf_release(&key);
579+
}
580+
581+
/* "unset X Y...;" */
582+
for (i = 0; i < envs.nr; i++) {
583+
const char *var = envs.items[i].string;
584+
const char *val = envs.items[i].util;
585+
586+
if (val || !getenv(var))
587+
continue;
588+
589+
if (!printed_unset) {
590+
strbuf_addstr(dst, " unset");
591+
printed_unset = 1;
592+
}
593+
strbuf_addf(dst, " %s", var);
594+
}
595+
if (printed_unset)
596+
strbuf_addch(dst, ';');
597+
598+
/* ... followed by "A=B C=D ..." */
599+
for (i = 0; i < envs.nr; i++) {
600+
const char *var = envs.items[i].string;
601+
const char *val = envs.items[i].util;
602+
const char *oldval;
603+
604+
if (!val)
605+
continue;
606+
607+
oldval = getenv(var);
608+
if (oldval && !strcmp(val, oldval))
609+
continue;
610+
611+
strbuf_addf(dst, " %s=", var);
612+
sq_quote_buf_pretty(dst, val);
613+
}
614+
string_list_clear(&envs, 0);
615+
}
616+
560617
static void trace_run_command(const struct child_process *cp)
561618
{
562619
struct strbuf buf = STRBUF_INIT;
@@ -565,6 +622,12 @@ static void trace_run_command(const struct child_process *cp)
565622
return;
566623

567624
strbuf_addf(&buf, "trace: run_command:");
625+
/*
626+
* The caller is responsible for initializing cp->env from
627+
* cp->env_array if needed. We only check one place.
628+
*/
629+
if (cp->env)
630+
trace_add_env(&buf, cp->env);
568631
if (cp->git_cmd)
569632
strbuf_addstr(&buf, " git");
570633
sq_quote_argv_pretty(&buf, cp->argv);

t/helper/test-run-command.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ int cmd_main(int argc, const char **argv)
5454
struct child_process proc = CHILD_PROCESS_INIT;
5555
int jobs;
5656

57+
if (argc < 3)
58+
return 1;
59+
while (!strcmp(argv[1], "env")) {
60+
if (!argv[2])
61+
die("env specifier without a value");
62+
argv_array_push(&proc.env_array, argv[2]);
63+
argv += 2;
64+
argc -= 2;
65+
}
5766
if (argc < 3)
5867
return 1;
5968
proc.argv = (const char **)argv + 2;

t/t0061-run-command.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,41 @@ test_expect_success 'run_command outputs ' '
141141
test_cmp expect actual
142142
'
143143

144+
test_trace () {
145+
expect="$1"
146+
shift
147+
GIT_TRACE=1 test-run-command "$@" run-command true 2>&1 >/dev/null | \
148+
sed 's/.* run_command: //' >actual &&
149+
echo "$expect true" >expect &&
150+
test_cmp expect actual
151+
}
152+
153+
test_expect_success 'GIT_TRACE with environment variables' '
154+
test_trace "abc=1 def=2" env abc=1 env def=2 &&
155+
test_trace "abc=2" env abc env abc=1 env abc=2 &&
156+
test_trace "abc=2" env abc env abc=2 &&
157+
(
158+
abc=1 && export abc &&
159+
test_trace "def=1" env abc=1 env def=1
160+
) &&
161+
(
162+
abc=1 && export abc &&
163+
test_trace "def=1" env abc env abc=1 env def=1
164+
) &&
165+
test_trace "def=1" env non-exist env def=1 &&
166+
test_trace "abc=2" env abc=1 env abc env abc=2 &&
167+
(
168+
abc=1 def=2 && export abc def &&
169+
test_trace "unset abc def;" env abc env def
170+
) &&
171+
(
172+
abc=1 def=2 && export abc def &&
173+
test_trace "unset def; abc=3" env abc env def env abc=3
174+
) &&
175+
(
176+
abc=1 && export abc &&
177+
test_trace "unset abc;" env abc=2 env abc
178+
)
179+
'
180+
144181
test_done

0 commit comments

Comments
 (0)