Skip to content

Commit 7e5d2ec

Browse files
committed
for-each-repo: work correctly in a worktree
When run in a worktree, the GIT_DIR directory is set in a different way than in a typical repository. Show this by updating t0068 to include a worktree and add a test that runs from that worktree. This requires moving the repo.key config into a global config instead of the base test repository's local config (demonstrating that it worked with non-worktree Git repositories). We need to be careful to unset the local Git environment variables and let the child process rediscover them, while also reinstating those variables in the parent process afterwards. Update run_command_on_repo() to store, unset, then reset the non-NULL variables. But... ...we also need to be careful to not un-set environment variables around -c config options or --no-replace-objects. A detailed comment includes the justification for these filters. Add tests that carefully check a few of these environment variables, including GIT_CONFIG_GLOBAL and GIT_CONFIG_PARAMETERS as examples. These tests do not provide full coverage, but cover enough to prevent a bug that existed in an earlier version of this change. Reported-by: Matthew Gabeler-Lee <fastcat@gmail.com> Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent 6e9d4f3 commit 7e5d2ec

2 files changed

Lines changed: 66 additions & 6 deletions

File tree

builtin/for-each-repo.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "builtin.h"
44
#include "config.h"
5+
#include "environment.h"
56
#include "gettext.h"
67
#include "parse-options.h"
78
#include "path.h"
@@ -15,10 +16,31 @@ static const char * const for_each_repo_usage[] = {
1516

1617
static int run_command_on_repo(const char *path, int argc, const char ** argv)
1718
{
18-
int i;
19+
int i = 0;
1920
struct child_process child = CHILD_PROCESS_INIT;
2021
char *abspath = interpolate_path(path, 0);
2122

23+
while (local_repo_env[i]) {
24+
/*
25+
* Preserve pre-builtin options:
26+
* - GIT_CONFIG, GIT_CONFIG_GLOBAL, and GIT_CONFIG_SYSTEM
27+
* point Git at different file locations for these scopes.
28+
* - GIT_CONFIG_PARAMETERS, and GIT_CONFIG_COUNT persist
29+
* -c <name>=<value> and --config-env=<name>=<envvar>
30+
* options.
31+
* - NO_REPLACE_OBJECTS_ENVIRONMENT persists the
32+
* --no-replace-objects option.
33+
*
34+
* Note that the following options are not in local_repo_env:
35+
* - EXEC_PATH_ENVIRONMENT persists --exec-path option.
36+
*/
37+
if (!starts_with(local_repo_env[i], "GIT_CONFIG") &&
38+
strcmp(local_repo_env[i], NO_REPLACE_OBJECTS_ENVIRONMENT))
39+
strvec_push(&child.env, local_repo_env[i]);
40+
41+
i++;
42+
}
43+
2244
child.git_cmd = 1;
2345
strvec_pushl(&child.args, "-C", abspath, NULL);
2446

t/t0068-for-each-repo.sh

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ TEST_NO_CREATE_REPO=1
88
. ./test-lib.sh
99

1010
test_expect_success 'run based on configured value' '
11-
git init one &&
12-
git init two &&
13-
git init three &&
14-
git init ~/four &&
11+
git init --initial-branch=one one &&
12+
git init --initial-branch=two two &&
13+
git -C two worktree add --orphan ../three &&
14+
git -C three checkout -b three &&
15+
git init --initial-branch=four ~/four &&
16+
1517
git -C two commit --allow-empty -m "DID NOT RUN" &&
1618
git config --global run.key "$TRASH_DIRECTORY/one" &&
1719
git config --global --add run.key "$TRASH_DIRECTORY/three" &&
@@ -35,7 +37,43 @@ test_expect_success 'run based on configured value' '
3537
git -C three log -1 --pretty=format:%s >message &&
3638
grep again message &&
3739
git -C ~/four log -1 --pretty=format:%s >message &&
38-
grep again message
40+
grep again message &&
41+
42+
git -C three for-each-repo --config=run.key -- \
43+
commit --allow-empty -m "ran from worktree" &&
44+
git -C one log -1 --pretty=format:%s >message &&
45+
test_grep "ran from worktree" message &&
46+
git -C two log -1 --pretty=format:%s >message &&
47+
test_grep ! "ran from worktree" message &&
48+
git -C three log -1 --pretty=format:%s >message &&
49+
test_grep "ran from worktree" message &&
50+
git -C ~/four log -1 --pretty=format:%s >message &&
51+
test_grep "ran from worktree" message &&
52+
53+
# Test running with config values set by environment
54+
cat >expect <<-EOF &&
55+
ran from worktree (HEAD -> refs/heads/one)
56+
ran from worktree (HEAD -> refs/heads/three)
57+
ran from worktree (HEAD -> refs/heads/four)
58+
EOF
59+
60+
GIT_CONFIG_PARAMETERS="${SQ}log.decorate=full${SQ}" \
61+
git -C three for-each-repo --config=run.key -- log --format="%s%d" -1 >out &&
62+
test_cmp expect out &&
63+
64+
cat >test-config <<-EOF &&
65+
[run]
66+
key = $(pwd)/one
67+
key = $(pwd)/three
68+
key = $(pwd)/four
69+
70+
[log]
71+
decorate = full
72+
EOF
73+
74+
GIT_CONFIG_GLOBAL="$(pwd)/test-config" \
75+
git -C three for-each-repo --config=run.key -- log --format="%s%d" -1 >out &&
76+
test_cmp expect out
3977
'
4078

4179
test_expect_success 'do nothing on empty config' '

0 commit comments

Comments
 (0)