Merge branch 'nd/conditional-config-include'
authorJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:46 +0000 (22:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:46 +0000 (22:07 -0700)
$GIT_DIR may in some cases be normalized with all symlinks resolved
while "gitdir" path expansion in the pattern does not receive the
same treatment, leading to incorrect mismatch.  This has been fixed.

* nd/conditional-config-include:
  config: resolve symlinks in conditional include's patterns
  path.c: and an option to call real_path() in expand_user_path()

builtin/commit.c
builtin/config.c
cache.h
config.c
credential-cache.c
credential-store.c
path.c
t/t1305-config-include.sh

index 4e288bc..ad188fe 100644 (file)
@@ -1404,7 +1404,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
 static const char *implicit_ident_advice(void)
 {
-       char *user_config = expand_user_path("~/.gitconfig");
+       char *user_config = expand_user_path("~/.gitconfig", 0);
        char *xdg_config = xdg_config_home("config");
        int config_exists = file_exists(user_config) || file_exists(xdg_config);
 
index 4f49a0e..3f7c876 100644 (file)
@@ -502,7 +502,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
 
        if (use_global_config) {
-               char *user_config = expand_user_path("~/.gitconfig");
+               char *user_config = expand_user_path("~/.gitconfig", 0);
                char *xdg_config = xdg_config_home("config");
 
                if (!user_config)
diff --git a/cache.h b/cache.h
index e5cc069..75cce81 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1164,7 +1164,7 @@ typedef int create_file_fn(const char *path, void *cb);
 int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 
 int mkdir_in_gitdir(const char *path);
-extern char *expand_user_path(const char *path);
+extern char *expand_user_path(const char *path, int real_home);
 const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
index aae6dcc..0daaed3 100644 (file)
--- a/config.c
+++ b/config.c
@@ -135,7 +135,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!path)
                return config_error_nonbool("include.path");
 
-       expanded = expand_user_path(path);
+       expanded = expand_user_path(path, 0);
        if (!expanded)
                return error("could not expand include path '%s'", path);
        path = expanded;
@@ -177,7 +177,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        char *expanded;
        int prefix = 0;
 
-       expanded = expand_user_path(pat->buf);
+       expanded = expand_user_path(pat->buf, 1);
        if (expanded) {
                strbuf_reset(pat);
                strbuf_addstr(pat, expanded);
@@ -191,7 +191,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
                        return error(_("relative config include "
                                       "conditionals must come from files"));
 
-               strbuf_add_absolute_path(&path, cf->path);
+               strbuf_realpath(&path, cf->path, 1);
                slash = find_last_dir_sep(path.buf);
                if (!slash)
                        die("BUG: how is this possible?");
@@ -213,7 +213,7 @@ static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
 
-       strbuf_add_absolute_path(&text, get_git_dir());
+       strbuf_realpath(&text, get_git_dir(), 1);
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
 
@@ -965,7 +965,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value)
 {
        if (!value)
                return config_error_nonbool(var);
-       *dest = expand_user_path(value);
+       *dest = expand_user_path(value, 0);
        if (!*dest)
                die(_("failed to expand user dir in: '%s'"), value);
        return 0;
@@ -1515,7 +1515,7 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
 {
        int ret = 0;
        char *xdg_config = xdg_config_home("config");
-       char *user_config = expand_user_path("~/.gitconfig");
+       char *user_config = expand_user_path("~/.gitconfig", 0);
        char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
index 3cbd420..91550bf 100644 (file)
@@ -87,7 +87,7 @@ static char *get_socket_path(void)
 {
        struct stat sb;
        char *old_dir, *socket;
-       old_dir = expand_user_path("~/.git-credential-cache");
+       old_dir = expand_user_path("~/.git-credential-cache", 0);
        if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode))
                socket = xstrfmt("%s/socket", old_dir);
        else
index 55ca1b1..ac29542 100644 (file)
@@ -168,7 +168,7 @@ int cmd_main(int argc, const char **argv)
        if (file) {
                string_list_append(&fns, file);
        } else {
-               if ((file = expand_user_path("~/.git-credentials")))
+               if ((file = expand_user_path("~/.git-credentials", 0)))
                        string_list_append_nodup(&fns, file);
                file = xdg_config_home("credentials");
                if (file)
diff --git a/path.c b/path.c
index 302f9af..c1cb1cf 100644 (file)
--- a/path.c
+++ b/path.c
@@ -617,8 +617,10 @@ static struct passwd *getpw_str(const char *username, size_t len)
  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
  * then it is a newly allocated string. Returns NULL on getpw failure or
  * if path is NULL.
+ *
+ * If real_home is true, real_path($HOME) is used in the expansion.
  */
-char *expand_user_path(const char *path)
+char *expand_user_path(const char *path, int real_home)
 {
        struct strbuf user_path = STRBUF_INIT;
        const char *to_copy = path;
@@ -633,7 +635,10 @@ char *expand_user_path(const char *path)
                        const char *home = getenv("HOME");
                        if (!home)
                                goto return_null;
-                       strbuf_addstr(&user_path, home);
+                       if (real_home)
+                               strbuf_addstr(&user_path, real_path(home));
+                       else
+                               strbuf_addstr(&user_path, home);
 #ifdef GIT_WINDOWS_NATIVE
                        convert_slashes(user_path.buf);
 #endif
@@ -702,7 +707,7 @@ const char *enter_repo(const char *path, int strict)
                strbuf_add(&validated_path, path, len);
 
                if (used_path.buf[0] == '~') {
-                       char *newpath = expand_user_path(used_path.buf);
+                       char *newpath = expand_user_path(used_path.buf, 0);
                        if (!newpath)
                                return NULL;
                        strbuf_attach(&used_path, newpath, strlen(newpath),
index e833939..8fbc7a0 100755 (executable)
@@ -3,6 +3,16 @@
 test_description='test config file include directives'
 . ./test-lib.sh
 
+# Force setup_explicit_git_dir() to run until the end. This is needed
+# by some tests to make sure real_path() is called on $GIT_DIR. The
+# caller needs to make sure git commands are run from a subdirectory
+# though or real_path() will not be called.
+force_setup_explicit_git_dir() {
+    GIT_DIR="$(pwd)/.git"
+    GIT_WORK_TREE="$(pwd)"
+    export GIT_DIR GIT_WORK_TREE
+}
+
 test_expect_success 'include file by absolute path' '
        echo "[test]one = 1" >one &&
        echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
@@ -208,6 +218,50 @@ test_expect_success 'conditional include, both unanchored, icase' '
        )
 '
 
+test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
+       mkdir real-home &&
+       ln -s real-home home &&
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME" &&
+
+               git init foo &&
+               cd foo &&
+               mkdir sub
+       )
+'
+
+test_expect_success SYMLINKS 'conditional include, $HOME expansion with symlinks' '
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME"/foo &&
+
+               echo "[includeIf \"gitdir:~/foo/\"]path=bar2" >>.git/config &&
+               echo "[test]two=2" >.git/bar2 &&
+               echo 2 >expect &&
+               force_setup_explicit_git_dir &&
+               git -C sub config test.two >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success SYMLINKS 'conditional include, relative path with symlinks' '
+       echo "[includeIf \"gitdir:./foo/.git\"]path=bar4" >home/.gitconfig &&
+       echo "[test]four=4" >home/bar4 &&
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME"/foo &&
+
+               echo 4 >expect &&
+               force_setup_explicit_git_dir &&
+               git -C sub config test.four >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig