config: match both symlink & realpath versions in IncludeIf.gitdir:*

Change the conditional inclusion mechanism to support e.g. gitdir:~/git_tree/repo where ~/git_tree is a symlink to /mnt/stuff/repo. This worked in the initial version of this facility[1], but regressed later in the series while solving a related bug[2]. Now gitdir: will match against the symlinked path (e.g. gitdir:~/git_tree/repo) in addition to the current /mnt/stuff/repo path. Since this is already in a release version note in the documentation that this behavior changed, so users who expect their configuration to work on both v2.13.0 and some future version of git with this fix aren't utterly confused. 1. commit 3efd0bedc6 ("config: add conditional include", 2017-03-01) 2. commit 86f9515708 ("config: resolve symlinks in conditional include's patterns", 2017-04-05) Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed May 16, 2017 at 08:28 UTC 0624c63ce6a488d3d8bccc4b1e79cc2093e2c4a4
3 files changed +49
Documentation/config.txt
+10
@@ -140,6 +140,16 @@ A few more notes on matching via `gitdir` and `gitdir/i`:
140
141 * Symlinks in `$GIT_DIR` are not resolved before matching.
142
143 + * Both the symlink & realpath versions of paths will be matched
144 + outside of `$GIT_DIR`. E.g. if ~/git is a symlink to
145 + /mnt/storage/git, both `gitdir:~/git` and `gitdir:/mnt/storage/git`
146 + will match.
147 ++
148 +This was not the case in the initial release of this feature in
149 +v2.13.0, which only matched the realpath version. Configuration that
150 +wants to be compatible with the initial release of this feature needs
151 +to either specify only the realpath version, or both versions.
152 +
153 * Note that "../" is not special and will match literally, which is
154 unlikely what you want.
155
config.c
+16
@@ -214,6 +214,7 @@ static int include_by_gitdir(const struct config_options *opts,
214 struct strbuf pattern = STRBUF_INIT;
215 int ret = 0, prefix;
216 const char *git_dir;
217 + int already_tried_absolute = 0;
218
219 if (opts->git_dir)
220 git_dir = opts->git_dir;
@@ -226,6 +227,7 @@ static int include_by_gitdir(const struct config_options *opts,
227 strbuf_add(&pattern, cond, cond_len);
228 prefix = prepare_include_condition_pattern(&pattern);
229
230 +again:
231 if (prefix < 0)
232 goto done;
233
@@ -245,6 +247,20 @@ static int include_by_gitdir(const struct config_options *opts,
247 ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
248 icase ? WM_CASEFOLD : 0, NULL);
249
250 + if (!ret && !already_tried_absolute) {
251 + /*
252 + * We've tried e.g. matching gitdir:~/work, but if
253 + * ~/work is a symlink to /mnt/storage/work
254 + * strbuf_realpath() will expand it, so the rule won't
255 + * match. Let's match against a
256 + * strbuf_add_absolute_path() version of the path,
257 + * which'll do the right thing
258 + */
259 + strbuf_reset(&text);
260 + strbuf_add_absolute_path(&text, git_dir);
261 + already_tried_absolute = 1;
262 + goto again;
263 + }
264 done:
265 strbuf_release(&pattern);
266 strbuf_release(&text);
t/t1305-config-include.sh
+23
@@ -273,6 +273,29 @@ test_expect_success SYMLINKS 'conditional include, relative path with symlinks'
273 )
274 '
275
276 +test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' '
277 + ln -s foo bar &&
278 + (
279 + cd bar &&
280 + echo "[includeIf \"gitdir:bar/\"]path=bar7" >>.git/config &&
281 + echo "[test]seven=7" >.git/bar7 &&
282 + echo 7 >expect &&
283 + git config test.seven >actual &&
284 + test_cmp expect actual
285 + )
286 +'
287 +
288 +test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icase' '
289 + (
290 + cd bar &&
291 + echo "[includeIf \"gitdir/i:BAR/\"]path=bar8" >>.git/config &&
292 + echo "[test]eight=8" >.git/bar8 &&
293 + echo 8 >expect &&
294 + git config test.eight >actual &&
295 + test_cmp expect actual
296 + )
297 +'
298 +
299 test_expect_success 'include cycles are detected' '
300 cat >.gitconfig <<-\EOF &&
301 [test]value = gitconfig