{"thread":{"id":"30660","subject":"[PATCH] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","startedAt":"2012-05-30T18:02:35Z","lastAt":"2012-05-31T07:26:54Z","messageCount":2,"participants":["Huynh Khoi Nguyen NGUYEN","Matthieu Moy"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"192502","messageId":"1338400955-22991-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30660","inReplyTo":null,"subject":"[PATCH] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-05-30T18:02:35Z","receivedAt":"2012-05-30T18:02:35Z","isPatch":true,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will read both in $XDG_CONFIG_HOME/git/config and in ~/.gitconfig in this order:\n.git/config > ~/.gitconfig > $XDG_CONFIG_HOME/git/config > /etc/gitconfig\nIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config will be used.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Lucien KONG <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Valentin DUPERRAY <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Thomas NGUY <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Franck JONAS <Franck.Jonas@ensimag.imag.fr>\n---\n Documentation/git-config.txt    |    9 +++-\n builtin/config.c                |   48 +++++++++++++++++----\n config.c                        |   21 +++++++--\n t/t1306-read-xdg-config-file.sh |   88 +++++++++++++++++++++++++++++++++++++++\n 4 files changed, 150 insertions(+), 16 deletions(-)\n create mode 100755 t/t1306-read-xdg-config-file.sh\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex d9463cb..30418fa 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -99,8 +99,8 @@ OPTIONS\n \tFor writing options: write to global ~/.gitconfig file rather than\n \tthe repository .git/config.\n +\n-For reading options: read only from global ~/.gitconfig rather than\n-from all available files.\n+For reading options: read only from global ~/.gitconfig then from\n+$XDG_CONFIG_HOME/git/config rather than from all available files.\n +\n See also <<FILES>>.\n \n@@ -194,7 +194,7 @@ See also <<FILES>>.\n FILES\n -----\n \n-If not set explicitly with '--file', there are three files where\n+If not set explicitly with '--file', there are four files where\n 'git config' will search for configuration options:\n \n $GIT_DIR/config::\n@@ -204,6 +204,9 @@ $GIT_DIR/config::\n \tUser-specific configuration file. Also called \"global\"\n \tconfiguration file.\n \n+$XDG_CONFIG_HOME/git/config::\n+\tSecond user-specific configuration file. ~/.gitconfig has priority.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..38dba4f 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -161,7 +161,7 @@ static int show_config(const char *key_, const char *value_, void *cb)\n static int get_value(const char *key_, const char *regex_)\n {\n \tint ret = -1;\n-\tchar *global = NULL, *repo_config = NULL;\n+\tchar *gitconfig_global = NULL, *xdg_global = NULL, *repo_config = NULL;\n \tconst char *system_wide = NULL, *local;\n \tstruct config_include_data inc = CONFIG_INCLUDE_INIT;\n \tconfig_fn_t fn;\n@@ -171,8 +171,15 @@ static int get_value(const char *key_, const char *regex_)\n \tif (!local) {\n \t\tconst char *home = getenv(\"HOME\");\n \t\tlocal = repo_config = git_pathdup(\"config\");\n-\t\tif (home)\n-\t\t\tglobal = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\tif (home) {\n+\t\t\tconst char *xdg_config_home = getenv(\"XDG_CONFIG_HOME\");\n+\t\t\tif (xdg_config_home)\n+\t\t\t\txdg_global = xstrdup(mkpath(\"%s/git/config\", xdg_config_home));\n+\t\t\telse\n+\t\t\t\txdg_global = xstrdup(mkpath(\"%s/.config/git/config\", home));\n+\n+\t\t\tgitconfig_global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\t}\n \t\tif (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n \t}\n@@ -229,15 +236,19 @@ static int get_value(const char *key_, const char *regex_)\n \n \tif (do_all && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n-\tif (do_all && global)\n-\t\tgit_config_from_file(fn, global, data);\n+\tif (do_all && xdg_global)\n+\t\tgit_config_from_file(fn, xdg_global, data);\n+\tif (do_all && gitconfig_global)\n+\t\tgit_config_from_file(fn, gitconfig_global, data);\n \tif (do_all)\n \t\tgit_config_from_file(fn, local, data);\n \tgit_config_from_parameters(fn, data);\n \tif (!do_all && !seen)\n \t\tgit_config_from_file(fn, local, data);\n-\tif (!do_all && !seen && global)\n-\t\tgit_config_from_file(fn, global, data);\n+\tif (!do_all && !seen && gitconfig_global)\n+\t\tgit_config_from_file(fn, gitconfig_global, data);\n+\tif (!do_all && !seen && xdg_global)\n+\t\tgit_config_from_file(fn, xdg_global, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -254,7 +265,8 @@ static int get_value(const char *key_, const char *regex_)\n \n free_strings:\n \tfree(repo_config);\n-\tfree(global);\n+\tfree(gitconfig_global);\n+\tfree(xdg_global);\n \treturn ret;\n }\n \n@@ -381,7 +393,25 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \tif (use_global_config) {\n \t\tchar *home = getenv(\"HOME\");\n \t\tif (home) {\n-\t\t\tchar *user_config = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\t\tchar *user_config;\n+\t\t\tconst char *gitconfig_path = mkpath(\"%s/.gitconfig\", home);\n+\t\t\tconst char *xdg_config_path = NULL;\n+\t\t\tconst char *xdg_config_home = NULL;\n+\n+\t\t\txdg_config_home = getenv(\"XDG_CONFIG_HOME\");\n+\t\t\tif (xdg_config_home)\n+\t\t\t\txdg_config_path = mkpath(\"%s/git/config\", xdg_config_home);\n+\t\t\telse\n+\t\t\t\txdg_config_path = mkpath(\"%s/.config/git/config\", home);\n+\n+\t\t\tif (access(gitconfig_path, R_OK) && !access(xdg_config_path, R_OK) &&\n+\t\t\t    (actions == ACTION_LIST ||\n+\t\t\t     actions == ACTION_GET_COLOR ||\n+\t\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\t\tuser_config = xstrdup(xdg_config_path);\n+\t\t\telse\n+\t\t\t\tuser_config = xstrdup(gitconfig_path);\n+\n \t\t\tgiven_config_file = user_config;\n \t\t} else {\n \t\t\tdie(\"$HOME not set\");\ndiff --git a/config.c b/config.c\nindex 71ef171..53557dc 100644\n--- a/config.c\n+++ b/config.c\n@@ -939,10 +939,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \n \thome = getenv(\"HOME\");\n \tif (home) {\n-\t\tchar buf[PATH_MAX];\n-\t\tchar *user_config = mksnpath(buf, sizeof(buf), \"%s/.gitconfig\", home);\n-\t\tif (!access(user_config, R_OK)) {\n-\t\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tconst char *gitconfig_path = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\tconst char *xdg_config_path = NULL;\n+\t\tconst char *xdg_config_home = NULL;\n+\n+\t\txdg_config_home = getenv(\"XDG_CONFIG_HOME\");\n+\t\tif (xdg_config_home)\n+\t\t\txdg_config_path = xstrdup(mkpath(\"%s/git/config\", xdg_config_home));\n+\t\telse\n+\t\t\txdg_config_path = xstrdup(mkpath(\"%s/.config/git/config\", home));\n+\n+\t\tif (!access(xdg_config_path, R_OK)) {\n+\t\t\tret += git_config_from_file(fn, xdg_config_path, data);\n+\t\t\tfound += 1;\n+\t\t}\n+\n+\t\tif (!access(gitconfig_path, R_OK)) {\n+\t\t\tret += git_config_from_file(fn, gitconfig_path, data);\n \t\t\tfound += 1;\n \t\t}\n \t}\ndiff --git a/t/t1306-read-xdg-config-file.sh b/t/t1306-read-xdg-config-file.sh\nnew file mode 100755\nindex 0000000..2ce5e4b\n--- /dev/null\n+++ b/t/t1306-read-xdg-config-file.sh\n@@ -0,0 +1,88 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas,\n+#\t\t     Thomas Nguy, Khoi Nguyen\n+#\t\t     Grenoble INP Ensimag\n+#\n+\n+test_description='possibility to read $XDG_CONFIG_HOME/git/config file'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read automatically: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\tgit config --global advice.statushints false &&\n+\tcat >expect <<-\\EOF &&\n+\t# On branch master\n+\t#\n+\t# Initial commit\n+\t#\n+\t# Untracked files:\n+\t#\t.config/\n+\t#\t.gitconfig\n+\t#\texpect\n+\t#\toutput\n+\tnothing added to commit but untracked files present\n+\tEOF\n+\tgit status >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'read automatically: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[user]\" >.gitconfig &&\n+\techo \"\tname = read_gitconfig\" >>.gitconfig &&\n+\tgit config --global advice.statushints true &&\n+\tcat >expect <<-\\EOF &&\n+\t# On branch master\n+\t#\n+\t# Initial commit\n+\t#\n+\t# Untracked files:\n+\t#   (use \"git add <file>...\" to include in what will be committed)\n+\t#\n+\t#\t.config/\n+\t#\t.gitconfig\n+\t#\texpect\n+\t#\toutput\n+\tnothing added to commit but untracked files present (use \"git add\" to track)\n+\tEOF\n+\tgit status >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo read_config >expect &&\n+\tgit config --get user.name >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[user]\" >.gitconfig &&\n+\techo \"\tname = read_gitconfig\" >>.gitconfig &&\n+\techo read_gitconfig >expect &&\n+\tgit config --get user.name >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo user.name=read_config >expect &&\n+\tgit config --global --list >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[user]\" >.gitconfig &&\n+\techo \"\tname = read_gitconfig\" >>.gitconfig &&\n+\techo user.name=read_gitconfig >expect &&\n+\tgit config --global --list >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_done\n-- \n1.7.8\n"},{"id":"192545","messageId":"vpqlik8q08x.fsf@bauges.imag.fr","threadId":"30660","inReplyTo":"1338400955-22991-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCH] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-05-31T07:26:54Z","receivedAt":"2012-05-31T07:26:54Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nwrites:\n\n> +$XDG_CONFIG_HOME/git/config::\n> +\tSecond user-specific configuration file. ~/.gitconfig has priority.\n\nYou should mention the behavior if $XDG_CONFIG_HOME is not set.\n\n> +\t\tif (home) {\n> +\t\t\tconst char *xdg_config_home = getenv(\"XDG_CONFIG_HOME\");\n> +\t\t\tif (xdg_config_home)\n> +\t\t\t\txdg_global = xstrdup(mkpath(\"%s/git/config\", xdg_config_home));\n> +\t\t\telse\n> +\t\t\t\txdg_global = xstrdup(mkpath(\"%s/.config/git/config\", home));\n\nShouldn't there be a helper function to get the path ~/.config/git/\nand then append config? You're already computing this path twice, and\nwe'll need more instances of it if we want to give default values to\ncore.excludesfile and core.attributesfile in this directory too.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"}]}