{"thread":{"id":"30663","subject":"[PATCHv2] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","startedAt":"2012-05-30T21:19:35Z","lastAt":"2012-07-16T16:56:38Z","messageCount":88,"participants":["Huynh Khoi Nguyen NGUYEN","Junio C Hamano","Ramsay Jones","Matthieu Moy","nguyenhu@minatec.inpg.fr","Erik Faye-Lund","David Aguilar","Huynh Khoi Nguyen Nguyen","Thomas Rast","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"192512","messageId":"1338412775-22840-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338400509-26087-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv2] 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-30T21:19:35Z","receivedAt":"2012-05-30T21:19:35Z","isPatch":false,"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 |   87 +++++++++++++++++++++++++++++++++++++++\n 4 files changed, 149 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..4cab38b\n--- /dev/null\n+++ b/t/t1306-read-xdg-config-file.sh\n@@ -0,0 +1,87 @@\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 \"[advice]\" >.config/git/config &&\n+\techo \"\tstatushints = false\" >>.config/git/config &&\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#\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 \"[advice]\" >.gitconfig &&\n+\techo \"\tstatushints = true\" >>.gitconfig &&\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 \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\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":"192516","messageId":"7vr4u1xrkp.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1338412775-22840-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv2] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-05-30T21:54:46Z","receivedAt":"2012-05-30T21:54:46Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Huynh Khoi Nguyen NGUYEN  <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nwrites:\n\n> From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n>\n> Git 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\n> If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config will be used.\n\nIs it just me who finds the above three lines extremely unreadable?\n\nAlso can you give this patch a bit more sensible title?\n\"Possibility to\" does not tell us much---anything is possible if you\nchange code after all.\n\nI see the patch does not touch the writing codepath, which is\nprobably a good thing, but the log message should explicitly state\nthat.\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\nI am not sure in what way $HOME/.gitconfig has \"priority\".\n\nYour proposed log message says that You read from $HOME/.gitconfig\nand then from $XDG_CONFIG_HOME/git/config, which means that any\nsingle-valued variable set in $HOME/.gitconfig will be overwritten\nby whatever is in $XDG_CONFIG_HOME/git/config, no?  That sounds like\nyou are giving priority to the latter to me.\n\nAnd for multi-valued variables, settings from both files are read,\nso there isn't much inherent priority between the two, except for\nvariables for which the definition order matters, of course.\n\nIf you read only from $HOME/.gitconfig if exists, and read from\n$XDG_CONFIG_HOME/git/config only when $HOME/.gitconfig does not,\nthen you are giving $HOME/.gitconfig a priority, but that is not\nwhat the patch is doing as far as I can tell.\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 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\nThis is logically wrong; even when you fail to read $HOME, you may\nbe able to read $XDG_CONFIG_HOME, no?  It shouldn't be nested inside\n\"if (home)\" at all, methinks.\n\nIt would be more like\n\n\tglobal = xdg_global = NULL;\n        if (HOME exists?)\n        \tglobal = $HOME/.gitconfig\n\tif (XDG_CONFIG_HOME exists?)\n        \txdg_global = $XDG_CONFIG_HOME/git/config\n\telse if (HOME exists?)\n        \txdg_global = $HOME/.config/git/config\n\nno?\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\");\n\nExactly the same comment applies here.\n\nYou seem to always write to $HOME/.gitconfig, so missing $HOME may\nbe an error if the action is to store, but if you are reading and if\n$XDG_CONFIG_HOME is set, you do not have to have $HOME set, no?\nEven when there is $HOME, if there is no $HOME/.gitconfig file, you\nwouldn't want to give an error, so missing $HOME environment should\nbe treated pretty much the same way as missing $HOME/.gitconfig file\nfor the purpose of reading, no?\n\n> diff --git a/config.c b/config.c\n> index 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\nExactly the same comment applies here, too.\n\nThe original that read from $HOME/.gitconfig was simple enough so\nhaving three copies of getenv(\"HOME\") was perfectly fine, but as you\nare introduce this much complexity to to decide which two files to\nread from, the code added this patch needs to be refactored and\nthree copies of the same logic need to be consolidated, I would have\nto say.\n"},{"id":"192583","messageId":"1338475242-21770-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338412775-22840-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv3] Read from XDG configuration file, not write","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-05-31T14:40:42Z","receivedAt":"2012-05-31T14:40:42Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to read in $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git will not be able to write in this new\nconfiguration file. If $XDG_CONFIG_HOME is either not set or empty,\n$HOME/.config/git/config will be used as new configuration file.\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    |   12 ++++-\n builtin/config.c                |   37 +++++++++++-----\n cache.h                         |    1 +\n config.c                        |    5 ++\n path.c                          |   12 +++++\n t/t1306-read-xdg-config-file.sh |   87 +++++++++++++++++++++++++++++++++++++++\n 6 files changed, 139 insertions(+), 15 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..004015a 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..3fb23a1 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@@ -172,7 +172,9 @@ static int get_value(const char *key_, const char *regex_)\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\t\tgitconfig_global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\tif (xdg_git_path(\"config\"))\n+\t\t\txdg_global = xdg_git_path(\"config\");\n \t\tif (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n \t}\n@@ -229,15 +231,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 +260,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@@ -380,12 +387,18 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \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\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\tconst char *gitconfig_path = mkpath(\"%s/.gitconfig\", home);\n+\t\tconst char *xdg_config_path = xdg_git_path(\"config\");\n+\n+\t\tif (access(gitconfig_path, R_OK) && !access(xdg_config_path, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config_path;\n+\t\telse if (home)\n+\t\t\tgiven_config_file = xstrdup(gitconfig_path);\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..b86d083 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -708,6 +708,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern char *xdg_git_path(char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..e08632f 100644\n--- a/config.c\n+++ b/config.c\n@@ -937,6 +937,11 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\n \n+\tif (!access(xdg_git_path(\"config\"), R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_git_path(\"config\"), data);\n+\t\tfound += 1;\n+\t}\n+\n \thome = getenv(\"HOME\");\n \tif (home) {\n \t\tchar buf[PATH_MAX];\ndiff --git a/path.c b/path.c\nindex 6f2aa69..7dbedb2 100644\n--- a/path.c\n+++ b/path.c\n@@ -122,6 +122,18 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+char *xdg_git_path(char *file)\n+{\n+\tconst char *home = getenv(\"HOME\");\n+\tconst char *xdg_config_home = getenv(\"XDG_CONFIG_HOME\");\n+\n+\tif (xdg_config_home)\n+\t\treturn xstrdup(mkpath(\"%s/git/%s\", xdg_config_home, file));\n+\tif (home)\n+\t\treturn xstrdup(mkpath(\"%s/.config/git/%s\", home, file));\n+\treturn NULL;\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-read-xdg-config-file.sh b/t/t1306-read-xdg-config-file.sh\nnew file mode 100755\nindex 0000000..4cab38b\n--- /dev/null\n+++ b/t/t1306-read-xdg-config-file.sh\n@@ -0,0 +1,87 @@\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 \"[advice]\" >.config/git/config &&\n+\techo \"\tstatushints = false\" >>.config/git/config &&\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#\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 \"[advice]\" >.gitconfig &&\n+\techo \"\tstatushints = true\" >>.gitconfig &&\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 \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\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":"192618","messageId":"7vr4u05ct0.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1338475242-21770-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv3] Read from XDG configuration file, not write","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-05-31T20:13:31Z","receivedAt":"2012-05-31T20:13:31Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Huynh Khoi Nguyen NGUYEN  <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nwrites:\n\n> From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n>\n> Git will be able to read in $XDG_CONFIG_HOME/git/config, a new\n\nGetting nicer.\n\n> @@ -172,7 +172,9 @@ static int get_value(const char *key_, const char *regex_)\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\t\tgitconfig_global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n> +\t\tif (xdg_git_path(\"config\"))\n> +\t\t\txdg_global = xdg_git_path(\"config\");\n>  \t\tif (git_config_system())\n>  \t\t\tsystem_wide = git_etc_gitconfig();\n>  \t}\n\nHrm, xdg_git_path() returns allocated memory, and each call site\nleaks its return value, no?\n\nI didn't mean a micro-helper function like xdg_git_path() when I\nsuggested refactoring.  I meant a helper that figures out all the\nnecessary bits in one go.  For example, can't the above call site\nlook more like this?\n\n        static int get_value(const char *key_, const char *regex_)\n        {\n                int ret = -1;\n                char *global = NULL, *xdg = NULL, *repo_config = NULL;\n                const char *system_wide = NULL, *local;\n                struct config_include_data inc = CONFIG_INCLUDE_INIT;\n                config_fn_t fn;\n                void *data;\n\n                local = given_config_file;\n                if (!local) {\n                        local = repo_config = git_pathdup(\"config\");\n                        if (git_config_system())\n                                system_wide = git_etc_gitconfig();\n\t\t\thome_config_paths(&global, &xdg);\n                }\n\t\t...\n\nAnd then the config.c::home_config_paths() may look like:\n\n\tvoid home_config_paths(char **global, char **xdg)\n\t{\n\t\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n                char *home = getenv(\"HOME\");\n                char *to_free = NULL;\n\t\t\n                if (!home) {\n\t\t\t*global = NULL;\n\t\t} else {\n\t\t\tif (!xdg_home) {\n\t                        to_free = strdup(mkpath(\"%s/.config\", home));\n\t\t\t\txdg_home = to_free;\n\t\t\t}\n\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n\t\t}\n                \n                if (!xdg_home)\n                \t*xdg = NULL;\n\t\telse\n\t\t\t*xdg = xstrdup(mkpath(\"%s/git/config\", xdg_home));\n\t\tfree(to_free);\n\t}\n\n\n> diff --git a/t/t1306-read-xdg-config-file.sh b/t/t1306-read-xdg-config-file.sh\n> new file mode 100755\n> index 0000000..4cab38b\n> --- /dev/null\n> +++ b/t/t1306-read-xdg-config-file.sh\n> @@ -0,0 +1,87 @@\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 \"[advice]\" >.config/git/config &&\n> +\techo \"\tstatushints = false\" >>.config/git/config &&\n> +\tcat >expect <<-\\EOF &&\n> +\t# On branch master\n> +\t#\n> +\t# Initial commit\n\nUse of \"status\" output as test vector is not such a good idea for\nmultiple reasons.\n\n - You are aware that the \"status hints\" feature is currently in\n   flux (somebody nearby is working on a patch);\n\n - Even without another topic that currently works on it, \"git\n   status\" output is for human consumption and subject to\n   improvement and also l10n.\n\n - \"status\" output lists tracked and untracked paths, which makes it\n   harder to later introduce new file to the test directory that are\n   needed for later tests, or rename ones that were used in earlier\n   versions of the test, without having to update all of these test\n   vectors.\n\n> +\t#\n> +\t# Untracked files:\n> +\t#\t.config/\n> +\t#\texpect\n> +\t#\toutput\n\nThis is a good example that shows why using \"status\" output as a\ntest vector is not a good idea.\n\nNotice you listed \"expect\" and \"output\" here.  As my next suggestion\nis to rename \"output\" to \"actual\" (we typically \"test_cmp expect actual\"),\nuse of \"git status\" output as a test vector would mean you would\nhave to update them whenever such a change is needed.\n\n> +\tnothing added to commit but untracked files present\n> +\tEOF\n> +\tgit status >output &&\n> +\ttest_cmp expect output\n> +'\n"},{"id":"192624","messageId":"4FC7EB78.5090903@ramsay1.demon.co.uk","threadId":"30663","inReplyTo":"7vr4u1xrkp.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv2] Possibility to read both from ~/.gitconfig and from $XDG_CONFIG_HOME/git/config","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-05-31T22:06:48Z","receivedAt":"2012-05-31T22:06:48Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"Junio C Hamano wrote:\n> Huynh Khoi Nguyen NGUYEN  <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n[...]\n\n>> diff --git a/config.c b/config.c\n>> index 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> Exactly the same comment applies here, too.\n\nI have not tried this patch (or the v3 version, which I haven't read yet), but\nis it likely that this has re-introduced the bug addressed by commit 05bab3ea\n(\"config.c: Fix a static buffer overwrite bug by avoiding mkpath()\", 19-11-2011)?.\n\nI don't know the answer, but I suspect that it may have done just that. (indeed, it\nmay well have made the bug more likely to appear).\n\n> \n> The original that read from $HOME/.gitconfig was simple enough so\n> having three copies of getenv(\"HOME\") was perfectly fine, but as you\n> are introduce this much complexity to to decide which two files to\n> read from, the code added this patch needs to be refactored and\n> three copies of the same logic need to be consolidated, I would have\n> to say.\n\nI agree. Also, using mksnpath() in the refactored code (rather than\nmkpath()) would be a good idea. :-P\n\nATB,\nRamsay Jones\n"},{"id":"192696","messageId":"1338585788-9764-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338475242-21770-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-01T21:23:08Z","receivedAt":"2012-06-01T21:23:08Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to read in $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git will not be able to write in this new\nconfiguration file. If core.excludesfile is not define, Git will read\nthe global exclude files in $XDG_CONFIG_HOME/git/ignore. Same goes for\ncore.attributesfile in $XDG_CONFIG_HOME/git/attributes. If\n$XDG_CONFIG_HOME is either not set or empty, $HOME/.config will be\nused.\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>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt    |   12 +++-\n attr.c                          |   10 +++\n builtin/config.c                |   28 ++++++---\n cache.h                         |    1 +\n config.c                        |   21 ++++---\n dir.c                           |    4 +\n path.c                          |   26 ++++++++\n t/t1306-read-xdg-config-file.sh |  133 +++++++++++++++++++++++++++++++++++++++\n 8 files changed, 214 insertions(+), 21 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..7e344a2 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 and 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/attr.c b/attr.c\nindex 303751f..441387f 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -497,6 +497,9 @@ static int git_attr_system(void)\n static void bootstrap_attr_stack(void)\n {\n \tstruct attr_stack *elem;\n+\tchar *xdg_attributes_file;\n+\n+\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n \n \tif (attr_stack)\n \t\treturn;\n@@ -522,6 +525,13 @@ static void bootstrap_attr_stack(void)\n \t\t\telem->prev = attr_stack;\n \t\t\tattr_stack = elem;\n \t\t}\n+\t} else if (!access(xdg_attributes_file, R_OK)) {\n+\t\telem = read_attr_from_file(xdg_attributes_file, 1);\n+\t\tif (elem) {\n+\t\t\telem->origin = NULL;\n+\t\t\telem->prev = attr_stack;\n+\t\t\tattr_stack = elem;\n+\t\t}\n \t}\n \n \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..da54fd1 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 *global = NULL, *xdg = 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@@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)\n \n \tlocal = given_config_file;\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 (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n+\t\thome_config_paths(&global, &xdg, \"config\");\n \t}\n \n \tif (use_key_regexp) {\n@@ -229,6 +227,8 @@ 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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (do_all && global)\n \t\tgit_config_from_file(fn, global, data);\n \tif (do_all)\n@@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)\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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_)\n free_strings:\n \tfree(repo_config);\n \tfree(global);\n+\tfree(xdg);\n \treturn ret;\n }\n \n@@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \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\tchar *user_config = NULL;\n+\t\tchar *xdg_config = NULL;\n+\n+\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n+\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config;\n+\t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..0632503 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -708,6 +708,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern void home_config_paths(char **global, char **xdg, char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..d1393b8 100644\n--- a/config.c\n+++ b/config.c\n@@ -929,7 +929,10 @@ int git_config_system(void)\n int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n {\n \tint ret = 0, found = 0;\n-\tconst char *home = NULL;\n+\tchar *xdg_config = NULL;\n+\tchar *user_config = NULL;\n+\n+\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\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\t\tfound += 1;\n-\t\t}\n+\tif (!access(xdg_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_config, data);\n+\t\tfound += 1;\n+\t}\n+\n+\tif (!access(user_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tfound += 1;\n \t}\n \n \tif (repo_config && !access(repo_config, R_OK)) {\ndiff --git a/dir.c b/dir.c\nindex ed1510f..e0c3589 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1234,13 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n void setup_standard_excludes(struct dir_struct *dir)\n {\n \tconst char *path;\n+\tchar *xdg_path;\n \n \tdir->exclude_per_dir = \".gitignore\";\n \tpath = git_path(\"info/exclude\");\n+\thome_config_paths(NULL, &xdg_path, \"ignore\");\n \tif (!access(path, R_OK))\n \t\tadd_excludes_from_file(dir, path);\n \tif (excludes_file && !access(excludes_file, R_OK))\n \t\tadd_excludes_from_file(dir, excludes_file);\n+\telse if (!access(xdg_path, R_OK))\n+\t\tadd_excludes_from_file(dir, xdg_path);\n }\n \n int remove_path(const char *name)\ndiff --git a/path.c b/path.c\nindex 6f2aa69..53f3f53 100644\n--- a/path.c\n+++ b/path.c\n@@ -122,6 +122,32 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+void home_config_paths(char **global, char **xdg, char *file)\n+{\n+\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n+\tchar *home = getenv(\"HOME\");\n+\tchar *to_free = NULL;\n+\n+\tif (!home) {\n+\t\tif (global)\n+\t\t\t*global = NULL;\n+\t} else {\n+\t\tif (!xdg_home) {\n+\t\t\tto_free = strdup(mkpath(\"%s/.config\", home));\n+\t\t\txdg_home = to_free;\n+\t\t}\n+\t\tif (global)\n+\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t}\n+\n+\tif (!xdg_home)\n+\t\t*xdg = NULL;\n+\telse\n+\t\t*xdg = xstrdup(mkpath(\"%s/git/%s\", xdg_home, file));\n+\n+\tfree(to_free);\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-read-xdg-config-file.sh b/t/t1306-read-xdg-config-file.sh\nnew file mode 100755\nindex 0000000..a8775c4\n--- /dev/null\n+++ b/t/t1306-read-xdg-config-file.sh\n@@ -0,0 +1,133 @@\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='Compatibility with $XDG_CONFIG_HOME/git/config /ignore and /attributes files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[alias]\" >.config/git/config &&\n+\techo \"\tmyalias = !echo in_config\" >>.config/git/config &&\n+\techo in_config >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[alias]\" >.gitconfig &&\n+\techo \"\tmyalias = !echo in_gitconfig\" >>.gitconfig &&\n+\techo in_gitconfig >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\techo read_config >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Exclusion of a file in the XDG ignore file' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >to_be_excluded &&\n+\tgit add to_be_excluded &&\n+\tgit rm --cached to_be_excluded &&\n+\tmkdir -p ../.config/git/ &&\n+\techo to_be_excluded >../.config/git/ignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in both XDG and local ignore files' '\n+\techo to_be_excluded >.gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in a non-XDG global ignore file' '\n+\techo >../.config/git/ignore &&\n+\trm .gitignore &&\n+\techo to_be_excluded >../my_gitignore &&\n+\tgit config core.excludesfile ../my_gitignore &&\n+\ttest_when_finished \"cd .. && rm -rf git && rm -r .config\" &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Checking attributes in the XDG attributes file' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >f &&\n+\tgit check-attr -a f >actual &&\n+\ttest_line_count -eq 0 actual &&\n+\tcd .. &&\n+\tmkdir -p .config/git/ &&\n+\techo \"f attr_f\" >.config/git/attributes &&\n+\tcd git &&\n+\techo \"f: attr_f: set\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in both XDG and local ignore files' '\n+\techo  \"f -attr_f\" >.gitattributes &&\n+\techo \"f: attr_f: unset\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n+\trm .gitattributes &&\n+\techo \"f attr_f=test\" >../my_gitattributes &&\n+\tgit config core.attributesfile ../my_gitattributes &&\n+\ttest_when_finished \"cd .. && rm -rf git && rm -r .config\" &&\n+\techo \"f: attr_f: test\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_done\n-- \n1.7.8\n"},{"id":"192711","messageId":"vpq7gvq9czb.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"1338585788-9764-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-02T11:20:40Z","receivedAt":"2012-06-02T11:20:40Z","isPatch":false,"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> Git will not be able to write in this new configuration file.\n\nThe use of the future is ambiguous, and since another patch will allow\nwriting, I'd rather rephrase it as e.g.\n\nGit currently does not write to this new configuration file.\n\n> If core.excludesfile is not define, Git will read the global exclude\n\ns/define/defined/\n\n> files in $XDG_CONFIG_HOME/git/ignore. Same goes for\n> core.attributesfile in $XDG_CONFIG_HOME/git/attributes. If\n> $XDG_CONFIG_HOME is either not set or empty, $HOME/.config will be\n> used.\n\nI'd prefer having two separate patches for the config file and for the\ntwo others. If ignore and attributes are simple enough, they may go to\nthe same patch, but ideally, there would be two separate patches again.\n\n> --- a/Documentation/git-config.txt\n> +++ b/Documentation/git-config.txt\n\nNo doc for core.excludesfile and core.attributesfile change :-(.\n\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -497,6 +497,9 @@ static int git_attr_system(void)\n>  static void bootstrap_attr_stack(void)\n>  {\n>  \tstruct attr_stack *elem;\n> +\tchar *xdg_attributes_file;\n> +\n> +\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n>  \n>  \tif (attr_stack)\n>  \t\treturn;\n> @@ -522,6 +525,13 @@ static void bootstrap_attr_stack(void)\n>  \t\t\telem->prev = attr_stack;\n>  \t\t\tattr_stack = elem;\n>  \t\t}\n> +\t} else if (!access(xdg_attributes_file, R_OK)) {\n> +\t\telem = read_attr_from_file(xdg_attributes_file, 1);\n> +\t\tif (elem) {\n> +\t\t\telem->origin = NULL;\n> +\t\t\telem->prev = attr_stack;\n> +\t\t\tattr_stack = elem;\n> +\t\t}\n>  \t}\n>  \n>  \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\n\nThe logic seems overly complex, and you duplicate the if() uselessly.\n\nWhy not just set the variable git_attributes_file before entering the\nif? Something like this:\n\ndiff --git a/attr.c b/attr.c\nindex 303751f..71dc472 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -515,6 +515,9 @@ static void bootstrap_attr_stack(void)\n \t\t}\n \t}\n \n+\tif (!git_attributes_file)\n+\t\tgit_attributes_file = \"foo\";\n+\n \tif (git_attributes_file) {\n \t\telem = read_attr_from_file(git_attributes_file, 1);\n \t\tif (elem) {\n\n(obviously replacing \"foo\" by the actual code involving\nhome_config_paths(..., \"attributes\")).\n\nDoing so, you may even get rid of the \"if (git_attributes_file)\" on the\nnext line.\n\nThen, the logic is really \"core.excludesfile defaults to the XDG file\",\nnot \"if such variable is set then do that else do something else\".\n\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1234,13 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n>  void setup_standard_excludes(struct dir_struct *dir)\n>  {\n>  \tconst char *path;\n> +\tchar *xdg_path;\n>  \n>  \tdir->exclude_per_dir = \".gitignore\";\n>  \tpath = git_path(\"info/exclude\");\n> +\thome_config_paths(NULL, &xdg_path, \"ignore\");\n>  \tif (!access(path, R_OK))\n>  \t\tadd_excludes_from_file(dir, path);\n>  \tif (excludes_file && !access(excludes_file, R_OK))\n>  \t\tadd_excludes_from_file(dir, excludes_file);\n> +\telse if (!access(xdg_path, R_OK))\n> +\t\tadd_excludes_from_file(dir, xdg_path);\n>  }\n\nSame remark here. Look at the patch I sent earlier to give a default\nvalue:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/133343/focus=133415\n\nFor example, you version reads from XDG file if core.excludesfile is\nset, but the file it points to doesn't exist. I don't think this is\nexpected.\n\n> +\techo foo >to_be_excluded &&\n> +\tgit add to_be_excluded &&\n> +\tgit rm --cached to_be_excluded &&\n\nErr, why add and remove it? You just need to create it, right?\n\n> +\tcd .. &&\n> +\tmkdir -p .config/git/ &&\n\nI don't like these relative references to $HOME. If you mean $HOME, why\nnot say\n\nmkdir -p $HOME/.config/git/\necho \"f attr_f\" >$HOME/.config/git/\n\n?\n\n(you don't even need to \"cd\")\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192715","messageId":"20120602175209.Horde.QpN5M3wdC4BPyjaps7w1bMA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"vpq7gvq9czb.fsf@bauges.imag.fr","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-02T15:52:09Z","receivedAt":"2012-06-02T15:52:09Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\n\nMatthieu Moy <Matthieu.Moy@grenoble-inp.fr> a écrit :\n\n> I'd prefer having two separate patches for the config file and for the\n> two others. If ignore and attributes are simple enough, they may go to\n> the same patch, but ideally, there would be two separate patches again.\n\nWe will separate this patch in three different patches.\n\n> No doc for core.excludesfile and core.attributesfile change :-(.\n\nIt will be done for the next patch ;)\n\n>> --- a/attr.c\n>> +++ b/attr.c\n>> @@ -497,6 +497,9 @@ static int git_attr_system(void)\n>>  static void bootstrap_attr_stack(void)\n>>  {\n>>  \tstruct attr_stack *elem;\n>> +\tchar *xdg_attributes_file;\n>> +\n>> +\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n>>\n>>  \tif (attr_stack)\n>>  \t\treturn;\n>> @@ -522,6 +525,13 @@ static void bootstrap_attr_stack(void)\n>>  \t\t\telem->prev = attr_stack;\n>>  \t\t\tattr_stack = elem;\n>>  \t\t}\n>> +\t} else if (!access(xdg_attributes_file, R_OK)) {\n>> +\t\telem = read_attr_from_file(xdg_attributes_file, 1);\n>> +\t\tif (elem) {\n>> +\t\t\telem->origin = NULL;\n>> +\t\t\telem->prev = attr_stack;\n>> +\t\t\tattr_stack = elem;\n>> +\t\t}\n>>  \t}\n>>\n>>  \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\n>\n> The logic seems overly complex, and you duplicate the if() uselessly.\n>\n> Why not just set the variable git_attributes_file before entering the\n> if? Something like this:\n>\n> diff --git a/attr.c b/attr.c\n> index 303751f..71dc472 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -515,6 +515,9 @@ static void bootstrap_attr_stack(void)\n>  \t\t}\n>  \t}\n>\n> +\tif (!git_attributes_file)\n> +\t\tgit_attributes_file = \"foo\";\n> +\n>  \tif (git_attributes_file) {\n>  \t\telem = read_attr_from_file(git_attributes_file, 1);\n>  \t\tif (elem) {\n>\n> (obviously replacing \"foo\" by the actual code involving\n> home_config_paths(..., \"attributes\")).\n>\n> Doing so, you may even get rid of the \"if (git_attributes_file)\" on the\n> next line.\n\nWe first thought to use an \"else if\" in order not to pointlessly check the\nexistence of the xdg_attributes_file (or double checking git_attributes_file)\nif git_attributes_file exists.\nBTW after checking the code more closely, we do not need to verify the  \nexistence of the xdg_attributes_file so it is indeed more clean to use  \nyour version, Thank.\n\n>> --- a/dir.c\n>> +++ b/dir.c\n>> @@ -1234,13 +1234,17 @@ int remove_dir_recursively(struct strbuf  \n>> *path, int flag)\n>>  void setup_standard_excludes(struct dir_struct *dir)\n>>  {\n>>  \tconst char *path;\n>> +\tchar *xdg_path;\n>>\n>>  \tdir->exclude_per_dir = \".gitignore\";\n>>  \tpath = git_path(\"info/exclude\");\n>> +\thome_config_paths(NULL, &xdg_path, \"ignore\");\n>>  \tif (!access(path, R_OK))\n>>  \t\tadd_excludes_from_file(dir, path);\n>>  \tif (excludes_file && !access(excludes_file, R_OK))\n>>  \t\tadd_excludes_from_file(dir, excludes_file);\n>> +\telse if (!access(xdg_path, R_OK))\n>> +\t\tadd_excludes_from_file(dir, xdg_path);\n>>  }\n> Same remark here. Look at the patch I sent earlier to give a default\n> value:\n>\n> http://thread.gmane.org/gmane.comp.version-control.git/133343/focus=133415\n>\n> For example, you version reads from XDG file if core.excludesfile is\n> set, but the file it points to doesn't exist. I don't think this is\n> expected.\n\nActually, it's the opposite. Our version only read from XDG file if  \ncore.excludesfile is not set.\nAfter checking your patch, it may be more logical to inialize the default\nvalue of excludes_file to the xdg_path as done for the attributes file.\n\n>> +\techo foo >to_be_excluded &&\n>> +\tgit add to_be_excluded &&\n>> +\tgit rm --cached to_be_excluded &&\n>\n> Err, why add and remove it? You just need to create it, right?\n\nIt was to check if to_be_excluded is indeed not ignored at the beginning\nof the test before ignoring it but that's seem a bit over-testing, I'll\nremove it.\n\n>> +\tcd .. &&\n>> +\tmkdir -p .config/git/ &&\n>\n> I don't like these relative references to $HOME. If you mean $HOME, why\n> not say\n>\n> mkdir -p $HOME/.config/git/\n> echo \"f attr_f\" >$HOME/.config/git/\n>\n> ?\n\nIt will be fixed but BTW where the tests are executed,\n$HOME has a weird behaviour:\n\necho $HOME and echo \"$HOME\"\n      both returns /.../t/trash directory.t1306-read-xdg-config-file\nbut   echo foo >$HOME   writes in ../t/trash\nwhile echo foo >\"$HOME\" writes in t/trash directory.t1306-read-xdg-config-file\nso \"$HOME\" is needed for the tests to work.\n"},{"id":"192730","messageId":"vpqtxyt4e6k.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"20120602175209.Horde.QpN5M3wdC4BPyjaps7w1bMA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-02T21:05:55Z","receivedAt":"2012-06-02T21:05:55Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"nguyenhu@minatec.inpg.fr writes:\n\n>>> --- a/dir.c\n>>> +++ b/dir.c\n>>> @@ -1234,13 +1234,17 @@ int remove_dir_recursively(struct strbuf  \n>>> *path, int flag)\n>>>  void setup_standard_excludes(struct dir_struct *dir)\n>>>  {\n>>>  \tconst char *path;\n>>> +\tchar *xdg_path;\n>>>\n>>>  \tdir->exclude_per_dir = \".gitignore\";\n>>>  \tpath = git_path(\"info/exclude\");\n>>> +\thome_config_paths(NULL, &xdg_path, \"ignore\");\n>>>  \tif (!access(path, R_OK))\n>>>  \t\tadd_excludes_from_file(dir, path);\n>>>  \tif (excludes_file && !access(excludes_file, R_OK))\n>>>  \t\tadd_excludes_from_file(dir, excludes_file);\n>>> +\telse if (!access(xdg_path, R_OK))\n>>> +\t\tadd_excludes_from_file(dir, xdg_path);\n>>>  }\n>> Same remark here. Look at the patch I sent earlier to give a default\n>> value:\n>>\n>> http://thread.gmane.org/gmane.comp.version-control.git/133343/focus=133415\n>>\n>> For example, you version reads from XDG file if core.excludesfile is\n>> set, but the file it points to doesn't exist. I don't think this is\n>> expected.\n>\n> Actually, it's the opposite. Our version only read from XDG file if  \n> core.excludesfile is not set.\n\nIt's what you want to do, but not what I read from the code. Your \"else\nif\" above is reachable if \"excludes_file && !access(excludes_file, R_OK)\"\nis false, which includes the case when excludes_file is set but does not\nexist.\n\nAnyway, this just shows that the logic is too complex, we shouldn't need\nthis discussion with simple enough code.\n\n> echo $HOME and echo \"$HOME\"\n>       both returns /.../t/trash directory.t1306-read-xdg-config-file\n> but   echo foo >$HOME   writes in ../t/trash\n> while echo foo >\"$HOME\" writes in t/trash directory.t1306-read-xdg-config-file\n> so \"$HOME\" is needed for the tests to work.\n\nIn general, $HOME means \"evaluate $HOME and do the whitespace splitting\nafter\", and \"$HOME\" means \"evaluate $HOME and don't do whitespace\nsplitting\". Since \"trash directory\" contains a space, you need the\nquoting.\n\nThere are places where quoting is not needed, though, and I think\nredirect is part of them. But I wouldn't be surprised if the different\nlevels of quoting when ran in test-lib broke that.\n\nMy advice: always quote variables, unless you have a very good reason\nnot to do so.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192768","messageId":"1338754481-27012-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338585788-9764-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv5 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-03T20:14:38Z","receivedAt":"2012-06-03T20:14:38Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to read from $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git currently does not write to this new\nconfiguration file. If $XDG_CONFIG_HOME is either not set or empty,\n$HOME/.config will be used.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin DUPERRAY <Valentin.DUPERRAY@ensimag.imag.fr>\nSigned-off-by: Franck JONAS <Franck.JONAS@ensimag.imag.fr>\nSigned-off-by: Thomas NGUY <Thomas.NGUY@ensimag.imag.fr>\nSigned-off-by: Lucien KONG <Lucien.KONG@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |   12 ++++++--\n builtin/config.c             |   28 ++++++++++++------\n cache.h                      |    1 +\n config.c                     |   21 ++++++++------\n path.c                       |   26 +++++++++++++++++\n t/t1306-xdg-files.sh         |   64 ++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 131 insertions(+), 21 deletions(-)\n create mode 100755 t/t1306-xdg-files.sh\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex d9463cb..7e344a2 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 and 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..da54fd1 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 *global = NULL, *xdg = 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@@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)\n \n \tlocal = given_config_file;\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 (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n+\t\thome_config_paths(&global, &xdg, \"config\");\n \t}\n \n \tif (use_key_regexp) {\n@@ -229,6 +227,8 @@ 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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (do_all && global)\n \t\tgit_config_from_file(fn, global, data);\n \tif (do_all)\n@@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)\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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_)\n free_strings:\n \tfree(repo_config);\n \tfree(global);\n+\tfree(xdg);\n \treturn ret;\n }\n \n@@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \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\tchar *user_config = NULL;\n+\t\tchar *xdg_config = NULL;\n+\n+\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n+\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config;\n+\t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..0632503 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -708,6 +708,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern void home_config_paths(char **global, char **xdg, char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..d1393b8 100644\n--- a/config.c\n+++ b/config.c\n@@ -929,7 +929,10 @@ int git_config_system(void)\n int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n {\n \tint ret = 0, found = 0;\n-\tconst char *home = NULL;\n+\tchar *xdg_config = NULL;\n+\tchar *user_config = NULL;\n+\n+\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\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\t\tfound += 1;\n-\t\t}\n+\tif (!access(xdg_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_config, data);\n+\t\tfound += 1;\n+\t}\n+\n+\tif (!access(user_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tfound += 1;\n \t}\n \n \tif (repo_config && !access(repo_config, R_OK)) {\ndiff --git a/path.c b/path.c\nindex 6f2aa69..53f3f53 100644\n--- a/path.c\n+++ b/path.c\n@@ -122,6 +122,32 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+void home_config_paths(char **global, char **xdg, char *file)\n+{\n+\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n+\tchar *home = getenv(\"HOME\");\n+\tchar *to_free = NULL;\n+\n+\tif (!home) {\n+\t\tif (global)\n+\t\t\t*global = NULL;\n+\t} else {\n+\t\tif (!xdg_home) {\n+\t\t\tto_free = strdup(mkpath(\"%s/.config\", home));\n+\t\t\txdg_home = to_free;\n+\t\t}\n+\t\tif (global)\n+\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t}\n+\n+\tif (!xdg_home)\n+\t\t*xdg = NULL;\n+\telse\n+\t\t*xdg = xstrdup(mkpath(\"%s/git/%s\", xdg_home, file));\n+\n+\tfree(to_free);\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nnew file mode 100755\nindex 0000000..77823fc\n--- /dev/null\n+++ b/t/t1306-xdg-files.sh\n@@ -0,0 +1,64 @@\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='Compatibility with $XDG_CONFIG_HOME/git/ files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[alias]\" >.config/git/config &&\n+\techo \"\tmyalias = !echo in_config\" >>.config/git/config &&\n+\techo in_config >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[alias]\" >.gitconfig &&\n+\techo \"\tmyalias = !echo in_gitconfig\" >>.gitconfig &&\n+\techo in_gitconfig >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\techo read_config >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\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 >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.7.8\n"},{"id":"192765","messageId":"1338754481-27012-2-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv5 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-03T20:14:39Z","receivedAt":"2012-06-03T20:14:39Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nIf core.excludesfile is not defined, its default value will be\n$XDG_CONFIG_HOME/git/ignore in order to follow XDG specification. If\n$XDG_CONFIG_HOME is either not set or emty, $HOME/.config will be\nused.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin DUPERRAY <Valentin.DUPERRAY@ensimag.imag.fr>\nSigned-off-by: Franck JONAS <Franck.JONAS@ensimag.imag.fr>\nSigned-off-by: Thomas NGUY <Thomas.NGUY@ensimag.imag.fr>\nSigned-off-by: Lucien KONG <Lucien.KONG@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt    |    4 +++-\n Documentation/gitignore.txt |    4 +++-\n dir.c                       |    7 ++++++-\n t/t1306-xdg-files.sh        |   23 +++++++++++++++++++++++\n 4 files changed, 35 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 915cb5a..bfaef2c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -483,7 +483,9 @@ core.excludesfile::\n \t'.git/info/exclude', git looks into this file for patterns\n \tof files which are not meant to be tracked.  \"`~/`\" is expanded\n \tto the value of `$HOME` and \"`~user/`\" to the specified user's\n-\thome directory.  See linkgit:gitignore[5].\n+\thome directory. Its default value is $XDG_CONFIG_HOME/git/ignore.\n+\tIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config will\n+\tbe used. See linkgit:gitignore[5].\n \n core.askpass::\n \tSome commands (e.g. svn and http interfaces) that interactively\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..05ee33f 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into\n the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to\n ignore in all situations (e.g., backup or temporary files generated by\n the user's editor of choice) generally go into a file specified by\n-`core.excludesfile` in the user's `~/.gitconfig`.\n+`core.excludesfile` in the user's `~/.gitconfig`. Its default value is\n+$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,\n+$HOME/.config will be used.\n \n The underlying git plumbing tools, such as\n 'git ls-files' and 'git read-tree', read\ndiff --git a/dir.c b/dir.c\nindex ed1510f..8c6f47f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1234,12 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n void setup_standard_excludes(struct dir_struct *dir)\n {\n \tconst char *path;\n+\tchar *xdg_path;\n \n \tdir->exclude_per_dir = \".gitignore\";\n \tpath = git_path(\"info/exclude\");\n+\tif (!excludes_file) {\n+\t\thome_config_paths(NULL, &xdg_path, \"ignore\");\n+\t\texcludes_file = xdg_path;\n+\t}\n \tif (!access(path, R_OK))\n \t\tadd_excludes_from_file(dir, path);\n-\tif (excludes_file && !access(excludes_file, R_OK))\n+\tif (!access(excludes_file, R_OK))\n \t\tadd_excludes_from_file(dir, excludes_file);\n }\n \ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 77823fc..1a1f07e 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -61,4 +61,27 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'Exclusion of a file in the XDG ignore file' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >to_be_excluded &&\n+\tmkdir -p \"$HOME\"/.config/git/ &&\n+\techo to_be_excluded >\"$HOME\"/.config/git/ignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+test_expect_success 'Exclusion in both XDG and local ignore files' '\n+\techo to_be_excluded >.gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+test_expect_success 'Exclusion in a non-XDG global ignore file' '\n+\techo >\"$HOME\"/.config/git/ignore &&\n+\trm .gitignore &&\n+\techo to_be_excluded >\"$HOME\"/my_gitignore &&\n+\tgit config core.excludesfile \"$HOME\"/my_gitignore &&\n+\ttest_when_finished \"cd \\\"$HOME\\\" && rm -rf git && rm -r .config\" &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n test_done\n-- \n1.7.8\n"},{"id":"192767","messageId":"1338754481-27012-3-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv5 3/4] Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-03T20:14:40Z","receivedAt":"2012-06-03T20:14:40Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nIf core.attributesfile is not defined, its default value will be\n$XDG_CONFIG_HOME/git/attributes in order to follow XDG\nspecification. If $XDG_CONFIG_HOME is either not set or emty,\n$HOME/.config will be used.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin DUPERRAY <Valentin.DUPERRAY@ensimag.imag.fr>\nSigned-off-by: Franck JONAS <Franck.JONAS@ensimag.imag.fr>\nSigned-off-by: Thomas NGUY <Thomas.NGUY@ensimag.imag.fr>\nSigned-off-by: Lucien KONG <Lucien.KONG@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt        |    4 +++-\n Documentation/gitattributes.txt |    2 ++\n attr.c                          |   17 ++++++++++-------\n t/t1306-xdg-files.sh            |   30 ++++++++++++++++++++++++++++++\n 4 files changed, 45 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex bfaef2c..9dc274c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -500,7 +500,9 @@ core.attributesfile::\n \tIn addition to '.gitattributes' (per-directory) and\n \t'.git/info/attributes', git looks into this file for attributes\n \t(see linkgit:gitattributes[5]). Path expansions are made the same\n-\tway as for `core.excludesfile`.\n+\tway as for `core.excludesfile`. Its default value is\n+\t$XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not\n+\tset or empty, $HOME/.config will be used.\n \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 80120ea..3339eef 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into\n `.gitattributes` files. Attributes that should affect all repositories\n for a single user should be placed in a file specified by the\n `core.attributesfile` configuration option (see linkgit:git-config[1]).\n+Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME\n+is either not set or empty, $HOME/.config will be used.\n Attributes for all users on a system should be placed in the\n `$(prefix)/etc/gitattributes` file.\n \ndiff --git a/attr.c b/attr.c\nindex 303751f..aef93d8 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -497,6 +497,7 @@ static int git_attr_system(void)\n static void bootstrap_attr_stack(void)\n {\n \tstruct attr_stack *elem;\n+\tchar *xdg_attributes_file;\n \n \tif (attr_stack)\n \t\treturn;\n@@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void)\n \t\t}\n \t}\n \n-\tif (git_attributes_file) {\n-\t\telem = read_attr_from_file(git_attributes_file, 1);\n-\t\tif (elem) {\n-\t\t\telem->origin = NULL;\n-\t\t\telem->prev = attr_stack;\n-\t\t\tattr_stack = elem;\n-\t\t}\n+\tif (!git_attributes_file) {\n+\t\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n+\t\tgit_attributes_file = xdg_attributes_file;\n+\t}\n+\telem = read_attr_from_file(git_attributes_file, 1);\n+\tif (elem) {\n+\t\telem->origin = NULL;\n+\t\telem->prev = attr_stack;\n+\t\tattr_stack = elem;\n \t}\n \n \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 1a1f07e..80621ed 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -84,4 +84,34 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '\n \ttest_must_fail git add to_be_excluded\n '\n \n+test_expect_success 'Checking attributes in the XDG attributes file' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >f &&\n+\tgit check-attr -a f >actual &&\n+\ttest_line_count -eq 0 actual &&\n+\tmkdir -p \"$HOME\"/.config/git/ &&\n+\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\techo \"f: attr_f: set\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Checking attributes in both XDG and local ignore files' '\n+\techo \"f -attr_f\" >.gitattributes &&\n+\techo \"f: attr_f: unset\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n+\trm .gitattributes &&\n+\techo \"f attr_f=test\" >\"$HOME\"/my_gitattributes &&\n+\tgit config core.attributesfile \"$HOME\"/my_gitattributes &&\n+\ttest_when_finished \"cd \\\"$HOME\\\" && rm -rf git && rm -r .config\" &&\n+\techo \"f: attr_f: test\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.8\n"},{"id":"192766","messageId":"1338754481-27012-4-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv5 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-03T20:14:41Z","receivedAt":"2012-06-03T20:14:41Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to write to $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification, if:\n- this file already exists, and\n- $HOME/.gitconfig file doesn't, and\n- global option is used.\nOtherwise Git writes to $HOME/.gitconfig with global option, as usual.\nIf you don't create $XDG_CONFIG_HOME/git/config, there is ABSOLUTELY\nNO change. Users can use this new file ONLY if they want.\nIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config will be\nused.\nAdvice for users who often come back to an old version of GIT:\nyou shouldn't create this file.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin DUPERRAY <Valentin.DUPERRAY@ensimag.imag.fr>\nSigned-off-by: Franck JONAS <Franck.JONAS@ensimag.imag.fr>\nSigned-off-by: Thomas NGUY <Thomas.NGUY@ensimag.imag.fr>\nSigned-off-by: Lucien KONG <Lucien.KONG@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |    3 ++-\n builtin/config.c             |    5 +----\n t/t1306-xdg-files.sh         |   18 ++++++++++++++++++\n 3 files changed, 21 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7e344a2..a2b4786 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -97,7 +97,8 @@ OPTIONS\n \n --global::\n \tFor writing options: write to global ~/.gitconfig file rather than\n-\tthe repository .git/config.\n+\tthe repository .git/config, write to $XDG_CONFIG_HOME/git/config file\n+\tif this file exists and ~/.gitconfig file doesn't.\n +\n For reading options: read only from global ~/.gitconfig and from\n $XDG_CONFIG_HOME/git/config rather than from all available files.\ndiff --git a/builtin/config.c b/builtin/config.c\nindex da54fd1..e8e1c0a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,10 +387,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n-\t\t    (actions == ACTION_LIST ||\n-\t\t     actions == ACTION_GET_COLOR ||\n-\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 80621ed..1ff709d 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -114,4 +114,22 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\t>.config/git/config &&\n+\trm .gitconfig &&\n+\tgit config --global user.name \"write_config\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_config\" >>expected &&\n+\ttest_cmp expected .config/git/config\n+'\n+\n+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected .gitconfig\n+'\n+\n test_done\n-- \n1.7.8\n"},{"id":"192795","messageId":"vpqpq9fs3oi.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-2-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv5 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-04T11:43:25Z","receivedAt":"2012-06-04T11:43:25Z","isPatch":false,"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> If core.excludesfile is not defined, its default value will be\n> $XDG_CONFIG_HOME/git/ignore in order to follow XDG specification. If\n> $XDG_CONFIG_HOME is either not set or emty, $HOME/.config will be\n> used.\n\nIt's not clear whether $HOME/.config will be used to replace\n$XDG_CONFIG_HOME or $XDG_CONFIG_HOME/git/ignore. I'd say explicitely\n$HOME/.config/git/ignore to avoid this.\n\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -483,7 +483,9 @@ core.excludesfile::\n>  \t'.git/info/exclude', git looks into this file for patterns\n>  \tof files which are not meant to be tracked.  \"`~/`\" is expanded\n>  \tto the value of `$HOME` and \"`~user/`\" to the specified user's\n> -\thome directory.  See linkgit:gitignore[5].\n> +\thome directory. Its default value is $XDG_CONFIG_HOME/git/ignore.\n> +\tIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config will\n> +\tbe used. See linkgit:gitignore[5].\n\nLikewise.\n\n> --- a/Documentation/gitignore.txt\n> +++ b/Documentation/gitignore.txt\n> @@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into\n>  the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to\n>  ignore in all situations (e.g., backup or temporary files generated by\n>  the user's editor of choice) generally go into a file specified by\n> -`core.excludesfile` in the user's `~/.gitconfig`.\n> +`core.excludesfile` in the user's `~/.gitconfig`. Its default value is\n> +$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,\n> +$HOME/.config will be used.\n\nLikewise.\n\nOtherwise, sounds good.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192831","messageId":"4FCCF63B.8070609@ramsay1.demon.co.uk","threadId":"30663","inReplyTo":"1338585788-9764-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-06-04T17:54:03Z","receivedAt":"2012-06-04T17:54:03Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"Huynh Khoi Nguyen NGUYEN wrote:\n> From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n> \n> Git will be able to read in $XDG_CONFIG_HOME/git/config, a new\n> configuration file following XDG specification. In the order of\n> reading, this file is between global configuration file and system\n> wide configuration file. Git will not be able to write in this new\n> configuration file. If core.excludesfile is not define, Git will read\n> the global exclude files in $XDG_CONFIG_HOME/git/ignore. Same goes for\n> core.attributesfile in $XDG_CONFIG_HOME/git/attributes. If\n> $XDG_CONFIG_HOME is either not set or empty, $HOME/.config will be\n> used.\n> \n> Signed-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> Signed-off-by: Lucien KONG <Lucien.Kong@ensimag.imag.fr>\n> Signed-off-by: Valentin DUPERRAY <Valentin.Duperray@ensimag.imag.fr>\n> Signed-off-by: Thomas NGUY <Thomas.Nguy@ensimag.imag.fr>\n> Signed-off-by: Franck JONAS <Franck.Jonas@ensimag.imag.fr>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n> ---\n>  Documentation/git-config.txt    |   12 +++-\n>  attr.c                          |   10 +++\n>  builtin/config.c                |   28 ++++++---\n>  cache.h                         |    1 +\n>  config.c                        |   21 ++++---\n>  dir.c                           |    4 +\n>  path.c                          |   26 ++++++++\n>  t/t1306-read-xdg-config-file.sh |  133 +++++++++++++++++++++++++++++++++++++++\n>  8 files changed, 214 insertions(+), 21 deletions(-)\n>  create mode 100755 t/t1306-read-xdg-config-file.sh\n> \n\n[...]\n\n> diff --git a/attr.c b/attr.c\n> index 303751f..441387f 100644\n> --- a/attr.c\n> +++ b/attr.c\n> @@ -497,6 +497,9 @@ static int git_attr_system(void)\n>  static void bootstrap_attr_stack(void)\n>  {\n>  \tstruct attr_stack *elem;\n> +\tchar *xdg_attributes_file;\n> +\n> +\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n\nwho free()'s xdg_attributes_file ?\n\n[...]\n\n> diff --git a/config.c b/config.c\n> index 71ef171..d1393b8 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -929,7 +929,10 @@ int git_config_system(void)\n>  int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n>  {\n>  \tint ret = 0, found = 0;\n> -\tconst char *home = NULL;\n> +\tchar *xdg_config = NULL;\n> +\tchar *user_config = NULL;\n> +\n> +\thome_config_paths(&user_config, &xdg_config, \"config\");\n\nwho free()'s user_config and xdg_config?\n\n[...]\n\n> diff --git a/dir.c b/dir.c\n> index ed1510f..e0c3589 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -1234,13 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n>  void setup_standard_excludes(struct dir_struct *dir)\n>  {\n>  \tconst char *path;\n> +\tchar *xdg_path;\n>  \n>  \tdir->exclude_per_dir = \".gitignore\";\n>  \tpath = git_path(\"info/exclude\");\n> +\thome_config_paths(NULL, &xdg_path, \"ignore\");\n\nditto xdg_path ;-)\n\n>  \tif (!access(path, R_OK))\n>  \t\tadd_excludes_from_file(dir, path);\n>  \tif (excludes_file && !access(excludes_file, R_OK))\n>  \t\tadd_excludes_from_file(dir, excludes_file);\n> +\telse if (!access(xdg_path, R_OK))\n> +\t\tadd_excludes_from_file(dir, xdg_path);\n>  }\n>  \n>  int remove_path(const char *name)\n> diff --git a/path.c b/path.c\n> index 6f2aa69..53f3f53 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -122,6 +122,32 @@ char *git_path(const char *fmt, ...)\n>  \treturn cleanup_path(pathname);\n>  }\n>  \n> +void home_config_paths(char **global, char **xdg, char *file)\n> +{\n> +\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n> +\tchar *home = getenv(\"HOME\");\n> +\tchar *to_free = NULL;\n> +\n> +\tif (!home) {\n> +\t\tif (global)\n> +\t\t\t*global = NULL;\n> +\t} else {\n> +\t\tif (!xdg_home) {\n> +\t\t\tto_free = strdup(mkpath(\"%s/.config\", home));\n> +\t\t\txdg_home = to_free;\n> +\t\t}\n> +\t\tif (global)\n> +\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n> +\t}\n> +\n> +\tif (!xdg_home)\n> +\t\t*xdg = NULL;\n> +\telse\n> +\t\t*xdg = xstrdup(mkpath(\"%s/git/%s\", xdg_home, file));\n> +\n> +\tfree(to_free);\n> +}\n> +\n\nSo, this re-introduces the bug addressed by commit 05bab3ea. The test number\nis now 29 (rather than 21) but the same test is failing; namely t3200-branch.sh\ntest #29 (git branch -m q q2 without config should succeed).\n\nIn order to fix the bug, I created the patch given below (on top of this patch).\n(Note that it does not address the above issues).\n\nHTH\n\nATB,\nRamsay Jones\n\n-- >8 --\nFrom: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSubject: [PATCH] path.c: Fix a static buffer overwrite bug by avoiding mkpath()\n\nThe v4 version of the \"Read (but not write) from XDG configuration,\nXDG attributes and XDG ignore files\" patch, re-introduced the bug\naddressed by commit 05bab3ea (\"config.c: Fix a static buffer overwrite\nbug by avoiding mkpath()\", 19-11-2011). Note that the patch refactored\nthe code to determine the user (or home) configuration filename into\na new function (home_config_paths()). In doing so, the new code once\nagain uses mkpath() rather than mksnpath().\n\nIn order to fix the bug, we introduce a new variation of the mkpath()\nfunction, mkpathdup(), which avoids the use of the internal static\nbuffers. As the name implies, the new function returns a pointer to\nthe pathname as a dynamically allocated string. It is the callers\nresponsibility to free the memory for the returned string.\n\nHaving introduced the new function, we can now replace the calls to\n'xstrdup(mkpath(...))' in the home_config_paths() function with a\ncall to mkpathdup() to achieve the same effect, without tickling the\noriginal bug.\n\n(Also, note that the 'xstrdup(mkpath(...))' pattern occurs in several\nother places in the source ...)\n\nSigned-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\n---\n cache.h |    2 ++\n path.c  |   20 +++++++++++++++++---\n 2 files changed, 19 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 0632503..fbba2d6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\n extern char *git_pathdup(const char *fmt, ...)\n \t__attribute__((format (printf, 1, 2)));\n+extern char *mkpathdup(const char *fmt, ...)\n+\t__attribute__((format (printf, 1, 2)));\n \n /* Return a statically allocated filename matching the sha1 signature */\n extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));\ndiff --git a/path.c b/path.c\nindex 53f3f53..ca29bdd 100644\n--- a/path.c\n+++ b/path.c\n@@ -87,6 +87,20 @@ char *git_pathdup(const char *fmt, ...)\n \treturn xstrdup(path);\n }\n \n+char *mkpathdup(const char *fmt, ...)\n+{\n+\tchar path[PATH_MAX];\n+\tva_list args;\n+\tunsigned len;\n+\n+\tva_start(args, fmt);\n+\tlen = vsnprintf(path, sizeof(path), fmt, args);\n+\tva_end(args);\n+\tif (len >= sizeof(path))\n+\t\treturn xstrdup(bad_path);\n+\treturn xstrdup(cleanup_path(path));\n+}\n+\n char *mkpath(const char *fmt, ...)\n {\n \tva_list args;\n@@ -133,17 +147,17 @@ void home_config_paths(char **global, char **xdg, char *file)\n \t\t\t*global = NULL;\n \t} else {\n \t\tif (!xdg_home) {\n-\t\t\tto_free = strdup(mkpath(\"%s/.config\", home));\n+\t\t\tto_free = mkpathdup(\"%s/.config\", home);\n \t\t\txdg_home = to_free;\n \t\t}\n \t\tif (global)\n-\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n+\t\t\t*global = mkpathdup(\"%s/.gitconfig\", home);\n \t}\n \n \tif (!xdg_home)\n \t\t*xdg = NULL;\n \telse\n-\t\t*xdg = xstrdup(mkpath(\"%s/git/%s\", xdg_home, file));\n+\t\t*xdg = mkpathdup(\"%s/git/%s\", xdg_home, file);\n \n \tfree(to_free);\n }\n-- \n1.7.10\n"},{"id":"192833","messageId":"7v62b6udg1.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"4FCCF63B.8070609@ramsay1.demon.co.uk","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-04T18:41:50Z","receivedAt":"2012-06-04T18:41:50Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ramsay Jones <ramsay@ramsay1.demon.co.uk> writes:\n\n> Subject: [PATCH] path.c: Fix a static buffer overwrite bug by avoiding mkpath()\n>\n> In order to fix the bug, we introduce a new variation of the mkpath()\n> function, mkpathdup(), which avoids the use of the internal static\n> buffers.\n\nShouldn't we aim a bit higher to also avoid the use of bounded\nbuffer?  Instead of returning bad_path, retry with lengthened buffer\nuntil we succeed, or something.\n\nBetter yet, internally use strbuf_vaddf().\n"},{"id":"192856","messageId":"vpq4nqqn5dl.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-4-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv5 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-04T21:17:58Z","receivedAt":"2012-06-04T21:17:58Z","isPatch":false,"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> +test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n> +\tmkdir -p .config/git &&\n> +\t>.config/git/config &&\n> +\trm .gitconfig &&\n> +\tgit config --global user.name \"write_config\" &&\n> +\techo \"[user]\" >expected &&\n> +\techo \"\tname = write_config\" >>expected &&\n> +\ttest_cmp expected .config/git/config\n> +'\n\nIt was suggested that you also write to the XDG file if the XDG\n_directory_ exists. After thinking about it, I tend to disagree with\nthis, because creating ~/.config/git/ignore would let Git write to the\nXDG configuration file as a side effect, which I find strange.\n\nAnyway, you may add the case where ~/.config/git/ exists but not the\nconfig file in it to your tests, to materialize the decision you have\ntaken on that point.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192896","messageId":"20120605141916.Horde.wKnrT3wdC4BPzflEUfYiRPA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"4FCCF63B.8070609@ramsay1.demon.co.uk","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-05T12:19:16Z","receivedAt":"2012-06-05T12:19:16Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nRamsay Jones <ramsay@ramsay1.demon.co.uk> a écrit :\n>\n> So, this re-introduces the bug addressed by commit 05bab3ea. The test number\n> is now 29 (rather than 21) but the same test is failing; namely  \n> t3200-branch.sh\n> test #29 (git branch -m q q2 without config should succeed).\n>\n> In order to fix the bug, I created the patch given below (on top of  \n> this patch).\n> (Note that it does not address the above issues).\n>\n> HTH\n>\n> ATB,\n> Ramsay Jones\n>\n> -- >8 --\n> From: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\n> Subject: [PATCH] path.c: Fix a static buffer overwrite bug by  \n> avoiding mkpath()\n>\n> The v4 version of the \"Read (but not write) from XDG configuration,\n> XDG attributes and XDG ignore files\" patch, re-introduced the bug\n> addressed by commit 05bab3ea (\"config.c: Fix a static buffer overwrite\n> bug by avoiding mkpath()\", 19-11-2011). Note that the patch refactored\n> the code to determine the user (or home) configuration filename into\n> a new function (home_config_paths()). In doing so, the new code once\n> again uses mkpath() rather than mksnpath().\n>\n> In order to fix the bug, we introduce a new variation of the mkpath()\n> function, mkpathdup(), which avoids the use of the internal static\n> buffers. As the name implies, the new function returns a pointer to\n> the pathname as a dynamically allocated string. It is the callers\n> responsibility to free the memory for the returned string.\n>\n> Having introduced the new function, we can now replace the calls to\n> 'xstrdup(mkpath(...))' in the home_config_paths() function with a\n> call to mkpathdup() to achieve the same effect, without tickling the\n> original bug.\n>\n> (Also, note that the 'xstrdup(mkpath(...))' pattern occurs in several\n> other places in the source ...)\n>\n> Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\n> ---\n>  cache.h |    2 ++\n>  path.c  |   20 +++++++++++++++++---\n>  2 files changed, 19 insertions(+), 3 deletions(-)\n>\n> diff --git a/cache.h b/cache.h\n> index 0632503..fbba2d6 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n,  \n> const char *fmt, ...)\n>  \t__attribute__((format (printf, 3, 4)));\n>  extern char *git_pathdup(const char *fmt, ...)\n>  \t__attribute__((format (printf, 1, 2)));\n> +extern char *mkpathdup(const char *fmt, ...)\n> +\t__attribute__((format (printf, 1, 2)));\n>\n>  /* Return a statically allocated filename matching the sha1 signature */\n>  extern char *mkpath(const char *fmt, ...) __attribute__((format  \n> (printf, 1, 2)));\n> diff --git a/path.c b/path.c\n> index 53f3f53..ca29bdd 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -87,6 +87,20 @@ char *git_pathdup(const char *fmt, ...)\n>  \treturn xstrdup(path);\n>  }\n>\n> +char *mkpathdup(const char *fmt, ...)\n> +{\n> +\tchar path[PATH_MAX];\n> +\tva_list args;\n> +\tunsigned len;\n> +\n> +\tva_start(args, fmt);\n> +\tlen = vsnprintf(path, sizeof(path), fmt, args);\n> +\tva_end(args);\n> +\tif (len >= sizeof(path))\n> +\t\treturn xstrdup(bad_path);\n> +\treturn xstrdup(cleanup_path(path));\n> +}\n> +\n>  char *mkpath(const char *fmt, ...)\n>  {\n>  \tva_list args;\n> @@ -133,17 +147,17 @@ void home_config_paths(char **global, char  \n> **xdg, char *file)\n>  \t\t\t*global = NULL;\n>  \t} else {\n>  \t\tif (!xdg_home) {\n> -\t\t\tto_free = strdup(mkpath(\"%s/.config\", home));\n> +\t\t\tto_free = mkpathdup(\"%s/.config\", home);\n>  \t\t\txdg_home = to_free;\n>  \t\t}\n>  \t\tif (global)\n> -\t\t\t*global = xstrdup(mkpath(\"%s/.gitconfig\", home));\n> +\t\t\t*global = mkpathdup(\"%s/.gitconfig\", home);\n>  \t}\n>\n>  \tif (!xdg_home)\n>  \t\t*xdg = NULL;\n>  \telse\n> -\t\t*xdg = xstrdup(mkpath(\"%s/git/%s\", xdg_home, file));\n> +\t\t*xdg = mkpathdup(\"%s/git/%s\", xdg_home, file);\n>\n>  \tfree(to_free);\n>  }\n> --\n> 1.7.10\n>\n\nThank you for having fixed this bug we have re-introduced with your  \npatch, we will add your modifications in our next v6 version.\n"},{"id":"192898","messageId":"20120605150432.Horde.RXb5CHwdC4BPzgPgu1YiviA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"vpq4nqqn5dl.fsf@bauges.imag.fr","subject":"Re: [PATCHv5 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-05T13:04:32Z","receivedAt":"2012-06-05T13:04:32Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nMatthieu Moy <Matthieu.Moy@grenoble-inp.fr> a écrit :\n\n> Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> writes:\n>\n>> +test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n>> +\tmkdir -p .config/git &&\n>> +\t>.config/git/config &&\n>> +\trm .gitconfig &&\n>> +\tgit config --global user.name \"write_config\" &&\n>> +\techo \"[user]\" >expected &&\n>> +\techo \"\tname = write_config\" >>expected &&\n>> +\ttest_cmp expected .config/git/config\n>> +'\n>\n> It was suggested that you also write to the XDG file if the XDG\n> _directory_ exists. After thinking about it, I tend to disagree with\n> this, because creating ~/.config/git/ignore would let Git write to the\n> XDG configuration file as a side effect, which I find strange.\n>\n> Anyway, you may add the case where ~/.config/git/ exists but not the\n> config file in it to your tests, to materialize the decision you have\n> taken on that point.\n>\n> --\n> Matthieu Moy\n> http://www-verimag.imag.fr/~moy/\n\nI agree. This case will be added in our next v6 version.\n"},{"id":"192899","messageId":"20120605151743.Horde.U7dqenwdC4BPzgb3V6TSvjA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"vpqpq9fs3oi.fsf@bauges.imag.fr","subject":"Re: [PATCHv5 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-05T13:17:43Z","receivedAt":"2012-06-05T13:17:43Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nMatthieu Moy <Matthieu.Moy@grenoble-inp.fr> a écrit :\n\n> Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> writes:\n>\n>> If core.excludesfile is not defined, its default value will be\n>> $XDG_CONFIG_HOME/git/ignore in order to follow XDG specification. If\n>> $XDG_CONFIG_HOME is either not set or emty, $HOME/.config will be\n>> used.\n>\n> It's not clear whether $HOME/.config will be used to replace\n> $XDG_CONFIG_HOME or $XDG_CONFIG_HOME/git/ignore. I'd say explicitely\n> $HOME/.config/git/ignore to avoid this.\n>\n>> --- a/Documentation/config.txt\n>> +++ b/Documentation/config.txt\n>\n> Likewise.\n>\n>> --- a/Documentation/gitignore.txt\n>> +++ b/Documentation/gitignore.txt\n>\n> Likewise.\n>\n> Otherwise, sounds good.\n>\n> --\n> Matthieu Moy\n> http://www-verimag.imag.fr/~moy/\n\nWe will also do the same changes with core.attributesfile, in our next  \nv6 version.\n"},{"id":"192988","messageId":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338754481-27012-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-06T13:21:22Z","receivedAt":"2012-06-06T13:21:22Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to read from $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git currently does not write to this new\nconfiguration file. If $XDG_CONFIG_HOME is either not set or empty,\n$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: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |   12 +++++--\n builtin/config.c             |   28 +++++++++++-----\n cache.h                      |    3 ++\n config.c                     |   23 ++++++++-----\n path.c                       |   40 ++++++++++++++++++++++++\n t/t1306-xdg-files.sh         |   70 ++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 155 insertions(+), 21 deletions(-)\n create mode 100755 t/t1306-xdg-files.sh\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex d9463cb..7e344a2 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 and 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..da54fd1 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 *global = NULL, *xdg = 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@@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)\n \n \tlocal = given_config_file;\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 (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n+\t\thome_config_paths(&global, &xdg, \"config\");\n \t}\n \n \tif (use_key_regexp) {\n@@ -229,6 +227,8 @@ 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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (do_all && global)\n \t\tgit_config_from_file(fn, global, data);\n \tif (do_all)\n@@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)\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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_)\n free_strings:\n \tfree(repo_config);\n \tfree(global);\n+\tfree(xdg);\n \treturn ret;\n }\n \n@@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \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\tchar *user_config = NULL;\n+\t\tchar *xdg_config = NULL;\n+\n+\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n+\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config;\n+\t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..fbba2d6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\n extern char *git_pathdup(const char *fmt, ...)\n \t__attribute__((format (printf, 1, 2)));\n+extern char *mkpathdup(const char *fmt, ...)\n+\t__attribute__((format (printf, 1, 2)));\n \n /* Return a statically allocated filename matching the sha1 signature */\n extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));\n@@ -708,6 +710,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern void home_config_paths(char **global, char **xdg, char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..d28a499 100644\n--- a/config.c\n+++ b/config.c\n@@ -929,7 +929,10 @@ int git_config_system(void)\n int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n {\n \tint ret = 0, found = 0;\n-\tconst char *home = NULL;\n+\tchar *xdg_config = NULL;\n+\tchar *user_config = NULL;\n+\n+\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\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\t\tfound += 1;\n-\t\t}\n+\tif (!access(xdg_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_config, data);\n+\t\tfound += 1;\n+\t}\n+\n+\tif (!access(user_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tfound += 1;\n \t}\n \n \tif (repo_config && !access(repo_config, R_OK)) {\n@@ -963,6 +966,8 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tbreak;\n \t}\n \n+\tfree(xdg_config);\n+\tfree(user_config);\n \treturn ret == 0 ? found : ret;\n }\n \ndiff --git a/path.c b/path.c\nindex 6f2aa69..ca29bdd 100644\n--- a/path.c\n+++ b/path.c\n@@ -87,6 +87,20 @@ char *git_pathdup(const char *fmt, ...)\n \treturn xstrdup(path);\n }\n \n+char *mkpathdup(const char *fmt, ...)\n+{\n+\tchar path[PATH_MAX];\n+\tva_list args;\n+\tunsigned len;\n+\n+\tva_start(args, fmt);\n+\tlen = vsnprintf(path, sizeof(path), fmt, args);\n+\tva_end(args);\n+\tif (len >= sizeof(path))\n+\t\treturn xstrdup(bad_path);\n+\treturn xstrdup(cleanup_path(path));\n+}\n+\n char *mkpath(const char *fmt, ...)\n {\n \tva_list args;\n@@ -122,6 +136,32 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+void home_config_paths(char **global, char **xdg, char *file)\n+{\n+\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n+\tchar *home = getenv(\"HOME\");\n+\tchar *to_free = NULL;\n+\n+\tif (!home) {\n+\t\tif (global)\n+\t\t\t*global = NULL;\n+\t} else {\n+\t\tif (!xdg_home) {\n+\t\t\tto_free = mkpathdup(\"%s/.config\", home);\n+\t\t\txdg_home = to_free;\n+\t\t}\n+\t\tif (global)\n+\t\t\t*global = mkpathdup(\"%s/.gitconfig\", home);\n+\t}\n+\n+\tif (!xdg_home)\n+\t\t*xdg = NULL;\n+\telse\n+\t\t*xdg = mkpathdup(\"%s/git/%s\", xdg_home, file);\n+\n+\tfree(to_free);\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nnew file mode 100755\nindex 0000000..5b971d9\n--- /dev/null\n+++ b/t/t1306-xdg-files.sh\n@@ -0,0 +1,70 @@\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='Compatibility with $XDG_CONFIG_HOME/git/ files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[alias]\" >.config/git/config &&\n+\techo \"\tmyalias = !echo in_config\" >>.config/git/config &&\n+\techo in_config >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[alias]\" >.gitconfig &&\n+\techo \"\tmyalias = !echo in_gitconfig\" >>.gitconfig &&\n+\techo in_gitconfig >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\techo read_config >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_done\n-- \n1.7.8\n"},{"id":"192986","messageId":"1338988885-21933-2-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv6 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-06T13:21:23Z","receivedAt":"2012-06-06T13:21:23Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nIf core.excludesfile is not defined, its default value will be\n$XDG_CONFIG_HOME/git/ignore in order to follow XDG specification. If\n$XDG_CONFIG_HOME is either not set or emty, $HOME/.config/git/ignore\nwill be used.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt    |    4 +++-\n Documentation/gitignore.txt |    4 +++-\n dir.c                       |    7 ++++++-\n t/t1306-xdg-files.sh        |   40 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 52 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 915cb5a..509bf25 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -483,7 +483,9 @@ core.excludesfile::\n \t'.git/info/exclude', git looks into this file for patterns\n \tof files which are not meant to be tracked.  \"`~/`\" is expanded\n \tto the value of `$HOME` and \"`~user/`\" to the specified user's\n-\thome directory.  See linkgit:gitignore[5].\n+\thome directory. Its default value is $XDG_CONFIG_HOME/git/ignore.\n+\tIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore\n+\twill be used. See linkgit:gitignore[5].\n \n core.askpass::\n \tSome commands (e.g. svn and http interfaces) that interactively\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..a5c9b93 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into\n the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to\n ignore in all situations (e.g., backup or temporary files generated by\n the user's editor of choice) generally go into a file specified by\n-`core.excludesfile` in the user's `~/.gitconfig`.\n+`core.excludesfile` in the user's `~/.gitconfig`. Its default value is\n+$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,\n+$HOME/.config/git/ignore will be used.\n \n The underlying git plumbing tools, such as\n 'git ls-files' and 'git read-tree', read\ndiff --git a/dir.c b/dir.c\nindex ed1510f..8c6f47f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1234,12 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n void setup_standard_excludes(struct dir_struct *dir)\n {\n \tconst char *path;\n+\tchar *xdg_path;\n \n \tdir->exclude_per_dir = \".gitignore\";\n \tpath = git_path(\"info/exclude\");\n+\tif (!excludes_file) {\n+\t\thome_config_paths(NULL, &xdg_path, \"ignore\");\n+\t\texcludes_file = xdg_path;\n+\t}\n \tif (!access(path, R_OK))\n \t\tadd_excludes_from_file(dir, path);\n-\tif (excludes_file && !access(excludes_file, R_OK))\n+\tif (!access(excludes_file, R_OK))\n \t\tadd_excludes_from_file(dir, excludes_file);\n }\n \ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 5b971d9..873bbcf 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -67,4 +67,44 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'\n '\n \n \n+test_expect_success 'Exclusion of a file in the XDG ignore file' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >to_be_excluded &&\n+\t\tmkdir -p \"$HOME\"/.config/git/ &&\n+\t\techo to_be_excluded >\"$HOME\"/.config/git/ignore &&\n+\t\ttest_must_fail git add to_be_excluded\n+\t)\n+'\n+\n+\n+test_expect_success 'Exclusion in both XDG and local ignore files' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >to_be_excluded &&\n+\t\techo to_be_excluded >\"$HOME\"/.config/git/ignore &&\n+\t\techo to_be_excluded >.gitignore &&\n+\t\ttest_must_fail git add to_be_excluded\n+\t)\n+'\n+\n+\n+test_expect_success 'Exclusion in a non-XDG global ignore file' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >to_be_excluded &&\n+\t\techo >\"$HOME\"/.config/git/ignore &&\n+\t\techo to_be_excluded >\"$HOME\"/my_gitignore &&\n+\t\tgit config core.excludesfile \"$HOME\"/my_gitignore &&\n+\t\ttest_must_fail git add to_be_excluded\n+\t)\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"192987","messageId":"1338988885-21933-3-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv6 3/4] Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-06T13:21:24Z","receivedAt":"2012-06-06T13:21:24Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nIf core.attributesfile is not defined, its default value will be\n$XDG_CONFIG_HOME/git/attributes in order to follow XDG\nspecification. If $XDG_CONFIG_HOME is either not set or emty,\n$HOME/.config/git/attributes will be used.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt        |    4 ++-\n Documentation/gitattributes.txt |    2 +\n attr.c                          |   17 ++++++++-----\n t/t1306-xdg-files.sh            |   47 +++++++++++++++++++++++++++++++++++++++\n 4 files changed, 62 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 509bf25..f691d8e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -500,7 +500,9 @@ core.attributesfile::\n \tIn addition to '.gitattributes' (per-directory) and\n \t'.git/info/attributes', git looks into this file for attributes\n \t(see linkgit:gitattributes[5]). Path expansions are made the same\n-\tway as for `core.excludesfile`.\n+\tway as for `core.excludesfile`. Its default value is\n+\t$XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not\n+\tset or empty, $HOME/.config/git/attributes will be used.\n \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 80120ea..484c614 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into\n `.gitattributes` files. Attributes that should affect all repositories\n for a single user should be placed in a file specified by the\n `core.attributesfile` configuration option (see linkgit:git-config[1]).\n+Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME\n+is either not set or empty, $HOME/.config/git/attributes will be used.\n Attributes for all users on a system should be placed in the\n `$(prefix)/etc/gitattributes` file.\n \ndiff --git a/attr.c b/attr.c\nindex 303751f..aef93d8 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -497,6 +497,7 @@ static int git_attr_system(void)\n static void bootstrap_attr_stack(void)\n {\n \tstruct attr_stack *elem;\n+\tchar *xdg_attributes_file;\n \n \tif (attr_stack)\n \t\treturn;\n@@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void)\n \t\t}\n \t}\n \n-\tif (git_attributes_file) {\n-\t\telem = read_attr_from_file(git_attributes_file, 1);\n-\t\tif (elem) {\n-\t\t\telem->origin = NULL;\n-\t\t\telem->prev = attr_stack;\n-\t\t\tattr_stack = elem;\n-\t\t}\n+\tif (!git_attributes_file) {\n+\t\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n+\t\tgit_attributes_file = xdg_attributes_file;\n+\t}\n+\telem = read_attr_from_file(git_attributes_file, 1);\n+\tif (elem) {\n+\t\telem->origin = NULL;\n+\t\telem->prev = attr_stack;\n+\t\tattr_stack = elem;\n \t}\n \n \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 873bbcf..eb0f40b 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -107,4 +107,51 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '\n '\n \n \n+test_expect_success 'Checking attributes in the XDG attributes file' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >f &&\n+\t\tgit check-attr -a f >actual &&\n+\t\ttest_line_count -eq 0 actual &&\n+\t\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\t\techo \"f: attr_f: set\" >expected &&\n+\t\tgit check-attr -a f >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+\n+test_expect_success 'Checking attributes in both XDG and local ignore files' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >f &&\n+\t\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\t\techo \"f -attr_f\" >.gitattributes &&\n+\t\techo \"f: attr_f: unset\" >expected &&\n+\t\tgit check-attr -a f >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+\n+test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n+\ttest_when_finished \"rm -rf git\" &&\n+\tgit init git &&\n+\t(\n+\t\tcd git &&\n+\t\techo foo >f &&\n+\t\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\t\techo \"f attr_f=test\" >\"$HOME\"/my_gitattributes &&\n+\t\tgit config core.attributesfile \"$HOME\"/my_gitattributes &&\n+\t\techo \"f: attr_f: test\" >expected &&\n+\t\tgit check-attr -a f >actual &&\n+\t\ttest_cmp expected actual\n+\t)\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"192985","messageId":"1338988885-21933-4-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen NGUYEN","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-06T13:21:25Z","receivedAt":"2012-06-06T13:21:25Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n\nGit will be able to write to $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification, if:\n- this file already exists, and\n- $HOME/.gitconfig file doesn't, and\n- global option is used.\nOtherwise Git writes to $HOME/.gitconfig with global option, as usual.\nIf you don't create $XDG_CONFIG_HOME/git/config, there is ABSOLUTELY\nNO change. Users can use this new file ONLY if they want.\nIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config\nwill be used.\nAdvice for users who often come back to an old version of GIT:\nyou shouldn't create this file.\n\nSigned-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |    3 ++-\n builtin/config.c             |    5 +----\n t/t1306-xdg-files.sh         |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 33 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7e344a2..a2b4786 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -97,7 +97,8 @@ OPTIONS\n \n --global::\n \tFor writing options: write to global ~/.gitconfig file rather than\n-\tthe repository .git/config.\n+\tthe repository .git/config, write to $XDG_CONFIG_HOME/git/config file\n+\tif this file exists and ~/.gitconfig file doesn't.\n +\n For reading options: read only from global ~/.gitconfig and from\n $XDG_CONFIG_HOME/git/config rather than from all available files.\ndiff --git a/builtin/config.c b/builtin/config.c\nindex da54fd1..e8e1c0a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,10 +387,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n-\t\t    (actions == ACTION_LIST ||\n-\t\t     actions == ACTION_GET_COLOR ||\n-\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex eb0f40b..dbf236a 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -154,4 +154,34 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n '\n \n \n+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\t>.config/git/config &&\n+\trm .gitconfig &&\n+\tgit config --global user.name \"write_config\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_config\" >>expected &&\n+\ttest_cmp expected .config/git/config\n+'\n+\n+\n+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected .gitconfig\n+'\n+\n+\n+test_expect_success 'write: ~/.config/git/ exists and config file doesn'\\''t' '\n+\trm .gitconfig &&\n+\trm .config/git/config &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected .gitconfig\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"193118","messageId":"7vpq9aenl0.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-07T22:58:35Z","receivedAt":"2012-06-07T22:58:35Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> +char *mkpathdup(const char *fmt, ...)\n> +{\n> +\tchar path[PATH_MAX];\n> +\tva_list args;\n> +\tunsigned len;\n> +\n> +\tva_start(args, fmt);\n> +\tlen = vsnprintf(path, sizeof(path), fmt, args);\n> +\tva_end(args);\n> +\tif (len >= sizeof(path))\n> +\t\treturn xstrdup(bad_path);\n> +\treturn xstrdup(cleanup_path(path));\n> +}\n\nHrmph. If a new helper is introduced anyway, wouldn't it be a better\nidea to get rid of the hardcoded PATH_MAX limitation, perhaps using\nstrbuf_vaddf() or something in the implementation of this function?\n\n> diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\n> new file mode 100755\n> index 0000000..5b971d9\n> --- /dev/null\n> +++ b/t/t1306-xdg-files.sh\n> @@ -0,0 +1,70 @@\n> ...\n> +test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n> +\trm .gitconfig &&\n\n\"rm -f .gitconfig\"; always consider the possibility that any part of\nthe previous test could have been omitted or failed.\n"},{"id":"193119","messageId":"7vk3ziem2n.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1338988885-21933-2-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv6 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-07T23:31:12Z","receivedAt":"2012-06-07T23:31:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Huynh Khoi Nguyen NGUYEN  <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nwrites:\n\n> From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n>\n> If core.excludesfile is not defined, its default value will be\n> $XDG_CONFIG_HOME/git/ignore in order to follow XDG specification.\n\nI find the above statement hard to understand and harder to agree\nwith.\n\nRight now, when core.excludesfile is not defined, the user does not\nhave to worry about any \"global\" ignore file to get in the way while\nworking on a specific project.  With this change, if the user does\nnot want random paths in the project that happens to match a pattern\nin this XDG_CONFIG_HOME/git/ignore file that is totally outside of\nthe project's control, the user needs to make sure core.excludesfile\nis set to an empty file or something.\n\nThat sounds like a huge downside; does XDG specification mandates\nsuch a regression?  Do they say \"you must treat XDG_CONFIG_HOME/git/ignore\nas your global ignore file\"?  It is hard to believe.\n\nPerhaps you are doing two or more unrelated things and stating what\nyou are doing poorly, which is what is confusing me?\n\nPerhaps the problem you are solving is something like this (by the\nway, you didn't state what problem you are trying to solve at all):\n\n - Use of core.excludesfile is an opt-in feature to keep ignore\n   patterns in a file and use it across projects defined per user.\n   To use this feature, the user needs to create such a file, and\n   add configuration variable to point at it.  The feature needs two\n   user actions to enable.\n\n - Instead, we can define a filename that is unlikely to already\n   exist on the system, and use the presense of the file as a cue\n   that the user wants to use that feature.  The user can simply\n   create the file without adding configuration variable to point at\n   it. Now the feature needs only one user action to enable, which\n   is the primary problem I wanted to solve.\n\n - Now we need to decide what that magic filename should be.  We\n   could pick any random filename that user can create, and no other\n   user can create for the user, which means that it has to be under\n   $HOME.  Let's use \"${XDG_CONFIG_HOME:-$HOME/.config/git}/ignore\";\n   it is as good as any random filename in $HOME/ and follows the\n   XDG guideline.\n\nBut that may not be what you meant to say.  I dunno.\n"},{"id":"193128","messageId":"vpqk3zi1978.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vk3ziem2n.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-08T08:47:39Z","receivedAt":"2012-06-08T08:47:39Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Perhaps the problem you are solving is something like this (by the\n> way, you didn't state what problem you are trying to solve at all):\n> [...]\n\nThat is a very good summary of what the patch tries to do. Following XDG\nis a nice side effect, but not the primary goal.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"193132","messageId":"20120608110238.Horde.s0zeY3wdC4BP0b_uCZzlJ3A@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7vk3ziem2n.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-08T09:02:38Z","receivedAt":"2012-06-08T09:02:38Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n> But that may not be what you meant to say.  I dunno.\n\nIt is exactly what we want to do. It is true the statement was very  \nshort and we didn't state what problem we are trying to solve. Your  \npoints raise the issues and explain clearly our solution.\n"},{"id":"193138","messageId":"20120608115711.Horde.-kmJPHwdC4BP0cx3RNFDX2A@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7vpq9aenl0.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-08T09:57:11Z","receivedAt":"2012-06-08T09:57:11Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n>> +char *mkpathdup(const char *fmt, ...)\n>> +{\n>> +\tchar path[PATH_MAX];\n>> +\tva_list args;\n>> +\tunsigned len;\n>> +\n>> +\tva_start(args, fmt);\n>> +\tlen = vsnprintf(path, sizeof(path), fmt, args);\n>> +\tva_end(args);\n>> +\tif (len >= sizeof(path))\n>> +\t\treturn xstrdup(bad_path);\n>> +\treturn xstrdup(cleanup_path(path));\n>> +}\n>\n> Hrmph. If a new helper is introduced anyway, wouldn't it be a better\n> idea to get rid of the hardcoded PATH_MAX limitation, perhaps using\n> strbuf_vaddf() or something in the implementation of this function?\n\nRamsay Jones, what do you think about this ?\n"},{"id":"193141","messageId":"20120608142601.Horde.Tq7UO3wdC4BP0e9ZGAy1EwA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7vpq9aenl0.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-08T12:26:01Z","receivedAt":"2012-06-08T12:26:01Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n>> +char *mkpathdup(const char *fmt, ...)\n>> +{\n>> +\tchar path[PATH_MAX];\n>> +\tva_list args;\n>> +\tunsigned len;\n>> +\n>> +\tva_start(args, fmt);\n>> +\tlen = vsnprintf(path, sizeof(path), fmt, args);\n>> +\tva_end(args);\n>> +\tif (len >= sizeof(path))\n>> +\t\treturn xstrdup(bad_path);\n>> +\treturn xstrdup(cleanup_path(path));\n>> +}\n>\n> Hrmph. If a new helper is introduced anyway, wouldn't it be a better\n> idea to get rid of the hardcoded PATH_MAX limitation, perhaps using\n> strbuf_vaddf() or something in the implementation of this function?\n\nWhat about this ?\n\nchar *mkpathdup(const char *fmt, ...)\n{\n\tchar *path;\n\tstruct strbuf sb = STRBUF_INIT;\n\tva_list args;\n\n\tva_start(args, fmt);\n\tstrbuf_vaddf(&sb, fmt, args);\n\tva_end(args);\n\tpath = sb.buf;\n\n\tstrbuf_release(&sb);\n\treturn xstrdup(cleanup_path(path));\n}\n"},{"id":"193142","messageId":"CABPQNSbSVznFbnm=iy4HdZ4PDC70UfbrqvvSKx9moy+9AOk+Rw@mail.gmail.com","threadId":"30663","inReplyTo":"20120608142601.Horde.Tq7UO3wdC4BP0e9ZGAy1EwA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-08T12:33:04Z","receivedAt":"2012-06-08T12:33:04Z","isPatch":false,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Fri, Jun 8, 2012 at 2:26 PM,  <nguyenhu@minatec.inpg.fr> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> a écrit :\n>\n>>> +char *mkpathdup(const char *fmt, ...)\n>>> +{\n>>> +       char path[PATH_MAX];\n>>> +       va_list args;\n>>> +       unsigned len;\n>>> +\n>>> +       va_start(args, fmt);\n>>> +       len = vsnprintf(path, sizeof(path), fmt, args);\n>>> +       va_end(args);\n>>> +       if (len >= sizeof(path))\n>>> +               return xstrdup(bad_path);\n>>> +       return xstrdup(cleanup_path(path));\n>>> +}\n>>\n>>\n>> Hrmph. If a new helper is introduced anyway, wouldn't it be a better\n>> idea to get rid of the hardcoded PATH_MAX limitation, perhaps using\n>> strbuf_vaddf() or something in the implementation of this function?\n>\n>\n> What about this ?\n>\n>\n> char *mkpathdup(const char *fmt, ...)\n> {\n>        char *path;\n>        struct strbuf sb = STRBUF_INIT;\n>        va_list args;\n>\n>        va_start(args, fmt);\n>        strbuf_vaddf(&sb, fmt, args);\n>        va_end(args);\n>        path = sb.buf;\n>\n>        strbuf_release(&sb);\n>        return xstrdup(cleanup_path(path));\n>\n> }\n\nNo, strbuf_release(&sb) frees 'sb.buf', causing 'path' to point to\nunallocated memory. You can fix that by doing something along these\nlines on top:\n\n \tva_end(args);\n-\tpath = sb.buf;\n+\tpath = xstrdup(cleanup_path(path));\n\n \tstrbuf_release(&sb);\n-\treturn xstrdup(cleanup_path(path));\n+\treturn path;\n }\n"},{"id":"193144","messageId":"20120608145423.Horde.qT2BG3wdC4BP0fX-GHdFAeA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"CABPQNSbSVznFbnm=iy4HdZ4PDC70UfbrqvvSKx9moy+9AOk+Rw@mail.gmail.com","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-08T12:54:23Z","receivedAt":"2012-06-08T12:54:23Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nErik Faye-Lund <kusmabite@gmail.com> a écrit :\n\n>> char *mkpathdup(const char *fmt, ...)\n>> {\n>>        char *path;\n>>        struct strbuf sb = STRBUF_INIT;\n>>        va_list args;\n>>\n>>        va_start(args, fmt);\n>>        strbuf_vaddf(&sb, fmt, args);\n>>        va_end(args);\n>>        path = sb.buf;\n>>\n>>        strbuf_release(&sb);\n>>        return xstrdup(cleanup_path(path));\n>>\n>> }\n>\n> No, strbuf_release(&sb) frees 'sb.buf', causing 'path' to point to\n> unallocated memory. You can fix that by doing something along these\n> lines on top:\n>\n>  \tva_end(args);\n> -\tpath = sb.buf;\n> +\tpath = xstrdup(cleanup_path(path));\n>\n>  \tstrbuf_release(&sb);\n> -\treturn xstrdup(cleanup_path(path));\n> +\treturn path;\n>  }\n\nYou are right, but I think you rather mean this, no?\n\n  \tva_end(args);\n-\tpath = sb.buf;\n+\tpath = xstrdup(cleanup_path(sb.buf));\n\n  \tstrbuf_release(&sb);\n-\treturn xstrdup(cleanup_path(path));\n+\treturn path;\n  }\n"},{"id":"193145","messageId":"CABPQNSYXDZ==NckcFw6BjGdZTD3HcNYFRKe4h8ZczRpGSACMJQ@mail.gmail.com","threadId":"30663","inReplyTo":"20120608145423.Horde.qT2BG3wdC4BP0fX-GHdFAeA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-08T12:57:42Z","receivedAt":"2012-06-08T12:57:42Z","isPatch":false,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Fri, Jun 8, 2012 at 2:54 PM,  <nguyenhu@minatec.inpg.fr> wrote:\n>\n> Erik Faye-Lund <kusmabite@gmail.com> a écrit :\n>\n>\n>>> char *mkpathdup(const char *fmt, ...)\n>>> {\n>>>        char *path;\n>>>        struct strbuf sb = STRBUF_INIT;\n>>>        va_list args;\n>>>\n>>>        va_start(args, fmt);\n>>>        strbuf_vaddf(&sb, fmt, args);\n>>>        va_end(args);\n>>>        path = sb.buf;\n>>>\n>>>        strbuf_release(&sb);\n>>>        return xstrdup(cleanup_path(path));\n>>>\n>>> }\n>>\n>>\n>> No, strbuf_release(&sb) frees 'sb.buf', causing 'path' to point to\n>> unallocated memory. You can fix that by doing something along these\n>> lines on top:\n>>\n>>        va_end(args);\n>> -       path = sb.buf;\n>> +       path = xstrdup(cleanup_path(path));\n>>\n>>        strbuf_release(&sb);\n>> -       return xstrdup(cleanup_path(path));\n>> +       return path;\n>>  }\n>\n>\n> You are right, but I think you rather mean this, no?\n>\n>\n>        va_end(args);\n> -       path = sb.buf;\n> +       path = xstrdup(cleanup_path(sb.buf));\n>\n>\n>        strbuf_release(&sb);\n> -       return xstrdup(cleanup_path(path));\n> +       return path;\n>  }\n>\n\nYes, sorry for the fat fingers :)\n"},{"id":"193161","messageId":"7vvcj1dep7.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"20120608142601.Horde.Tq7UO3wdC4BP0e9ZGAy1EwA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-08T15:08:04Z","receivedAt":"2012-06-08T15:08:04Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"nguyenhu@minatec.inpg.fr writes:\n\n> What about this ?\n\nWhat about that ;-)?\n\n> char *mkpathdup(const char *fmt, ...)\n> {\n> \tchar *path;\n> \tstruct strbuf sb = STRBUF_INIT;\n> \tva_list args;\n>\n> \tva_start(args, fmt);\n> \tstrbuf_vaddf(&sb, fmt, args);\n> \tva_end(args);\n> \tpath = sb.buf;\n>\n> \tstrbuf_release(&sb);\n> \treturn xstrdup(cleanup_path(path));\n> }\n\nModulo\n\n\tpath = strbuf_detach(&sb, NULL);\n\nthat is more or less what I meant.\n"},{"id":"193201","messageId":"CAJDDKr6sCrwy99-5-sOaAuNjYmnw73CicF3PyYMe8hvf38oFvg@mail.gmail.com","threadId":"30663","inReplyTo":"1338988885-21933-4-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-06-09T03:48:49Z","receivedAt":"2012-06-09T03:48:49Z","isPatch":false,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Wed, Jun 6, 2012 at 6:21 AM, Huynh Khoi Nguyen NGUYEN\n<Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr> wrote:\n> From: NGUYEN Huynh Khoi Nguyen <nguyenhu@ensibm.imag.fr>\n>\n> Git will be able to write to $XDG_CONFIG_HOME/git/config, a new\n> configuration file following XDG specification, if:\n> - this file already exists, and\n> - $HOME/.gitconfig file doesn't, and\n> - global option is used.\n> Otherwise Git writes to $HOME/.gitconfig with global option, as usual.\n> If you don't create $XDG_CONFIG_HOME/git/config, there is ABSOLUTELY\n> NO change. Users can use this new file ONLY if they want.\n> If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config\n> will be used.\n> Advice for users who often come back to an old version of GIT:\n> you shouldn't create this file.\n>\n> Signed-off-by: Huynh Khoi Nguyen NGUYEN <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\n> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\n> Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\n> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n> ---\n\nI'm all for this change too.\n\nI did a little research RE: the OSX and Windows question.\n\nglib makes no differentiation between OSX and Linux,\nbut Windows does have its own #ifdef.\n\nhttp://git.gnome.org/browse/glib/tree/glib/gutils.c#n1251\n\nI certainly don't think this should affect this series,\nI'm just noting it as something that the Windows folks might\ncare about.  Perhaps something for compat/ in the future?\n\nThe downside to doing the same as glib is more documentation.\nThe upside is... consistency?\n\nThanks,\nDavid\n\n\n>  Documentation/git-config.txt |    3 ++-\n>  builtin/config.c             |    5 +----\n>  t/t1306-xdg-files.sh         |   30 ++++++++++++++++++++++++++++++\n>  3 files changed, 33 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> index 7e344a2..a2b4786 100644\n> --- a/Documentation/git-config.txt\n> +++ b/Documentation/git-config.txt\n> @@ -97,7 +97,8 @@ OPTIONS\n>\n>  --global::\n>        For writing options: write to global ~/.gitconfig file rather than\n> -       the repository .git/config.\n> +       the repository .git/config, write to $XDG_CONFIG_HOME/git/config file\n> +       if this file exists and ~/.gitconfig file doesn't.\n>  +\n>  For reading options: read only from global ~/.gitconfig and from\n>  $XDG_CONFIG_HOME/git/config rather than from all available files.\n> diff --git a/builtin/config.c b/builtin/config.c\n> index da54fd1..e8e1c0a 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -387,10 +387,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>\n>                home_config_paths(&user_config, &xdg_config, \"config\");\n>\n> -               if (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n> -                   (actions == ACTION_LIST ||\n> -                    actions == ACTION_GET_COLOR ||\n> -                    actions == ACTION_GET_COLORBOOL))\n> +               if (access(user_config, R_OK) && !access(xdg_config, R_OK))\n>                        given_config_file = xdg_config;\n>                else if (user_config)\n>                        given_config_file = user_config;\n> diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\n> index eb0f40b..dbf236a 100755\n> --- a/t/t1306-xdg-files.sh\n> +++ b/t/t1306-xdg-files.sh\n> @@ -154,4 +154,34 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n>  '\n>\n>\n> +test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n> +       mkdir -p .config/git &&\n> +       >.config/git/config &&\n> +       rm .gitconfig &&\n> +       git config --global user.name \"write_config\" &&\n> +       echo \"[user]\" >expected &&\n> +       echo \"  name = write_config\" >>expected &&\n> +       test_cmp expected .config/git/config\n> +'\n> +\n> +\n> +test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '\n> +       >.gitconfig &&\n> +       git config --global user.name \"write_gitconfig\" &&\n> +       echo \"[user]\" >expected &&\n> +       echo \"  name = write_gitconfig\" >>expected &&\n> +       test_cmp expected .gitconfig\n> +'\n> +\n> +\n> +test_expect_success 'write: ~/.config/git/ exists and config file doesn'\\''t' '\n> +       rm .gitconfig &&\n> +       rm .config/git/config &&\n> +       git config --global user.name \"write_gitconfig\" &&\n> +       echo \"[user]\" >expected &&\n> +       echo \"  name = write_gitconfig\" >>expected &&\n> +       test_cmp expected .gitconfig\n> +'\n> +\n> +\n>  test_done\n> --\n> 1.7.8\n"},{"id":"193203","messageId":"7vipf1aty2.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"CAJDDKr6sCrwy99-5-sOaAuNjYmnw73CicF3PyYMe8hvf38oFvg@mail.gmail.com","subject":"Re: [PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-09T06:19:17Z","receivedAt":"2012-06-09T06:19:17Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> I'm all for this change too.\n\nDo you mean just the general direction, or including the\nimplementation?\n\n> I did a little research RE: the OSX and Windows question.\n>\n> glib makes no differentiation between OSX and Linux,\n> but Windows does have its own #ifdef.\n>\n> http://git.gnome.org/browse/glib/tree/glib/gutils.c#n1251\n>\n> I certainly don't think this should affect this series,\n> I'm just noting it as something that the Windows folks might\n> care about.  Perhaps something for compat/ in the future?\n>\n> The downside to doing the same as glib is more documentation.\n> The upside is... consistency?\n\nProbably.  I think we should follow whatever existing and prevalent\npractice is, and my gut feeling is that we would end up first doing\nPOSIX only thing in my tree, and msysgit folks will quickly feed me\nupdates to tweak the behaviour to match what of Windows version.\n"},{"id":"193206","messageId":"20120609125336.Horde.iUq0R3wdC4BP0yswpGlGBZA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7vvcj1dep7.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-09T10:53:36Z","receivedAt":"2012-06-09T10:53:36Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n>> char *mkpathdup(const char *fmt, ...)\n>> {\n>> \tchar *path;\n>> \tstruct strbuf sb = STRBUF_INIT;\n>> \tva_list args;\n>>\n>> \tva_start(args, fmt);\n>> \tstrbuf_vaddf(&sb, fmt, args);\n>> \tva_end(args);\n>> \tpath = sb.buf;\n>>\n>> \tstrbuf_release(&sb);\n>> \treturn xstrdup(cleanup_path(path));\n>> }\n>\n> Modulo\n>\n> \tpath = strbuf_detach(&sb, NULL);\n>\n> that is more or less what I meant.\n\nSo now the mkpathdup() function looks like:\n\nchar *mkpathdup(const char *fmt, ...)\n{\n\tchar *path;\n\tstruct strbuf sb = STRBUF_INIT;\n\tva_list args;\n\n\tva_start(args, fmt);\n\tstrbuf_vaddf(&sb, fmt, args);\n\tva_end(args);\n\tpath = strbuf_detach(&sb, NULL);\n\n\tstrbuf_release(&sb);\n\treturn path;\n}\n\nThis new variation of mkpathdup() function both fix the bug addressed\nby commit 05bab3ea and avoid the use of bounded buffer.\n"},{"id":"193209","messageId":"CAJDDKr4bKhWgXEfVGBEyw=HcuYm-jE5K3daDkJU_9NDsurGBUg@mail.gmail.com","threadId":"30663","inReplyTo":"7vipf1aty2.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-06-09T17:25:55Z","receivedAt":"2012-06-09T17:25:55Z","isPatch":false,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Fri, Jun 8, 2012 at 11:19 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> David Aguilar <davvid@gmail.com> writes:\n>\n>> I'm all for this change too.\n>\n> Do you mean just the general direction, or including the\n> implementation?\n\nGeneral direction, including the backwards-compatibility\nconcerns described in the commit message.\n\n\n>> I did a little research RE: the OSX and Windows question.\n>>\n>> glib makes no differentiation between OSX and Linux,\n>> but Windows does have its own #ifdef.\n>>\n>> http://git.gnome.org/browse/glib/tree/glib/gutils.c#n1251\n>>\n>> I certainly don't think this should affect this series,\n>> I'm just noting it as something that the Windows folks might\n>> care about.  Perhaps something for compat/ in the future?\n>>\n>> The downside to doing the same as glib is more documentation.\n>> The upside is... consistency?\n>\n> Probably.  I think we should follow whatever existing and prevalent\n> practice is, and my gut feeling is that we would end up first doing\n> POSIX only thing in my tree, and msysgit folks will quickly feed me\n> updates to tweak the behaviour to match what of Windows version.\n\nThat sounds good to me.\nThanks,\n-- \nDavid\n"},{"id":"193226","messageId":"7v4nqjbrdo.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"20120609125336.Horde.iUq0R3wdC4BP0yswpGlGBZA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-10T06:41:39Z","receivedAt":"2012-06-10T06:41:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"nguyenhu@minatec.inpg.fr writes:\n\n>> Modulo\n>>\n>> \tpath = strbuf_detach(&sb, NULL);\n>>\n>> that is more or less what I meant.\n>\n> So now the mkpathdup() function looks like:\n>\n> char *mkpathdup(const char *fmt, ...)\n> {\n> \tchar *path;\n> \tstruct strbuf sb = STRBUF_INIT;\n> \tva_list args;\n>\n> \tva_start(args, fmt);\n> \tstrbuf_vaddf(&sb, fmt, args);\n> \tva_end(args);\n> \tpath = strbuf_detach(&sb, NULL);\n>\n> \tstrbuf_release(&sb);\n> \treturn path;\n> }\n>\n> This new variation of mkpathdup() function both fix the bug addressed\n> by commit 05bab3ea and avoid the use of bounded buffer.\n\nI didn't mean to suggest removing the call to clean-up-path\nfunction.  What I meant was that strbuf_detach() is a way to take\nthe ownership of the buffer, so that you do not have to call\nstrbuf_release() on it.\n"},{"id":"193260","messageId":"vpqmx4bmhem.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vipf1aty2.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-10T13:21:37Z","receivedAt":"2012-06-10T13:21:37Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Probably.  I think we should follow whatever existing and prevalent\n> practice is, and my gut feeling is that we would end up first doing\n> POSIX only thing in my tree, and msysgit folks will quickly feed me\n> updates to tweak the behaviour to match what of Windows version.\n\nI don't really have opinion on what Git should do on windows, but in any\ncase, the current patches only changes the behavior if the XDG file\nexists, so it may be suboptimal on windows, but shouldn't harm windows\nusers.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"193262","messageId":"20120610154850.Horde.gYrJO3wdC4BP1KXCOvLEiCA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7v4nqjbrdo.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-10T13:48:50Z","receivedAt":"2012-06-10T13:48:50Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n>> char *mkpathdup(const char *fmt, ...)\n>> {\n>> \tchar *path;\n>> \tstruct strbuf sb = STRBUF_INIT;\n>> \tva_list args;\n>>\n>> \tva_start(args, fmt);\n>> \tstrbuf_vaddf(&sb, fmt, args);\n>> \tva_end(args);\n>> \tpath = strbuf_detach(&sb, NULL);\n>>\n>> \tstrbuf_release(&sb);\n>> \treturn path;\n>> }\n>\n> I didn't mean to suggest removing the call to clean-up-path\n> function.  What I meant was that strbuf_detach() is a way to take\n> the ownership of the buffer, so that you do not have to call\n> strbuf_release() on it.\n\nSo with the call to clean-up-path function and without the call to\nstrbuf_release(), mkpathdup() function becomes :\n\nchar *mkpathdup(const char *fmt, ...)\n{\n\tstruct strbuf sb = STRBUF_INIT;\n\tva_list args;\n\n\tva_start(args, fmt);\n\tstrbuf_vaddf(&sb, fmt, args);\n\tva_end(args);\n\n\treturn cleanup_path(strbuf_detach(&sb, NULL));\n}\n"},{"id":"193278","messageId":"CABPQNSYd0hCU8nrfKJJjR+T_jQn4WC6jEaSCJPjKsuPdumEdtw@mail.gmail.com","threadId":"30663","inReplyTo":"20120610154850.Horde.gYrJO3wdC4BP1KXCOvLEiCA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-10T18:44:26Z","receivedAt":"2012-06-10T18:44:26Z","isPatch":false,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Sun, Jun 10, 2012 at 3:48 PM,  <nguyenhu@minatec.inpg.fr> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> a écrit :\n>\n>>> char *mkpathdup(const char *fmt, ...)\n>>> {\n>>>        char *path;\n>>>        struct strbuf sb = STRBUF_INIT;\n>>>        va_list args;\n>>>\n>>>        va_start(args, fmt);\n>>>        strbuf_vaddf(&sb, fmt, args);\n>>>        va_end(args);\n>>>        path = strbuf_detach(&sb, NULL);\n>>>\n>>>        strbuf_release(&sb);\n>>>        return path;\n>>> }\n>>\n>>\n>> I didn't mean to suggest removing the call to clean-up-path\n>> function.  What I meant was that strbuf_detach() is a way to take\n>> the ownership of the buffer, so that you do not have to call\n>> strbuf_release() on it.\n>\n>\n> So with the call to clean-up-path function and without the call to\n> strbuf_release(), mkpathdup() function becomes :\n>\n>\n> char *mkpathdup(const char *fmt, ...)\n> {\n>        struct strbuf sb = STRBUF_INIT;\n>        va_list args;\n>\n>        va_start(args, fmt);\n>        strbuf_vaddf(&sb, fmt, args);\n>        va_end(args);\n>\n>        return cleanup_path(strbuf_detach(&sb, NULL));\n>\n> }\n\nThe awkward thing about doing this, is that the memory allocated by\nthe strbuf cannot be reclaimed if you go with this. A pointer that has\nbeen adjusted (like cleanup_path can do) cannot be successfully fed to\nfree.\n"},{"id":"193279","messageId":"20120610220239.Horde.UcGZQnwdC4BP1P1fPg1GEMA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"CABPQNSYd0hCU8nrfKJJjR+T_jQn4WC6jEaSCJPjKsuPdumEdtw@mail.gmail.com","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-10T20:02:39Z","receivedAt":"2012-06-10T20:02:39Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nErik Faye-Lund <kusmabite@gmail.com> a écrit :\n\n>> char *mkpathdup(const char *fmt, ...)\n>> {\n>>        struct strbuf sb = STRBUF_INIT;\n>>        va_list args;\n>>\n>>        va_start(args, fmt);\n>>        strbuf_vaddf(&sb, fmt, args);\n>>        va_end(args);\n>>\n>>        return cleanup_path(strbuf_detach(&sb, NULL));\n>>\n>> }\n>\n> The awkward thing about doing this, is that the memory allocated by\n> the strbuf cannot be reclaimed if you go with this. A pointer that has\n> been adjusted (like cleanup_path can do) cannot be successfully fed to\n> free.\n\nDo you mean that the previous version is preferable in keeping  \nclean-up-path function ?\n"},{"id":"193281","messageId":"CABPQNSZzErckfN8JjiSExUyVa+uGRJT0aut3gvtbGObnhQeowA@mail.gmail.com","threadId":"30663","inReplyTo":"20120610220239.Horde.UcGZQnwdC4BP1P1fPg1GEMA@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-10T20:27:26Z","receivedAt":"2012-06-10T20:27:26Z","isPatch":false,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Sun, Jun 10, 2012 at 10:02 PM,  <nguyenhu@minatec.inpg.fr> wrote:\n>\n> Erik Faye-Lund <kusmabite@gmail.com> a écrit :\n>\n>>> char *mkpathdup(const char *fmt, ...)\n>>> {\n>>>        struct strbuf sb = STRBUF_INIT;\n>>>        va_list args;\n>>>\n>>>        va_start(args, fmt);\n>>>        strbuf_vaddf(&sb, fmt, args);\n>>>        va_end(args);\n>>>\n>>>        return cleanup_path(strbuf_detach(&sb, NULL));\n>>>\n>>> }\n>>\n>>\n>> The awkward thing about doing this, is that the memory allocated by\n>> the strbuf cannot be reclaimed if you go with this. A pointer that has\n>> been adjusted (like cleanup_path can do) cannot be successfully fed to\n>> free.\n>\n>\n> Do you mean that the previous version is preferable in keeping clean-up-path\n> function ?\n>\n\nNo, that wasn't my intention. Since this is only used for a few config\nfiles, I don't think leaking the memory is a big deal. But it's\nprobably worth putting a comment in the code about it, to warn\npotential future users.\n"},{"id":"193318","messageId":"7v8vft97a8.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"CABPQNSYd0hCU8nrfKJJjR+T_jQn4WC6jEaSCJPjKsuPdumEdtw@mail.gmail.com","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-11T15:50:55Z","receivedAt":"2012-06-11T15:50:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Erik Faye-Lund <kusmabite@gmail.com> writes:\n\n> The awkward thing about doing this, is that the memory allocated by\n> the strbuf cannot be reclaimed if you go with this. A pointer that has\n> been adjusted (like cleanup_path can do) cannot be successfully fed to\n> free.\n\nYeah, I wouldn't recommend doing that.  Either\n\n\tpath = strbuf_detach(&sb, NULL);\n\tretval = xstrdup(cleanup_path(path));\n        free(path);\n        return retval;\n\nor\n\n\tpath = xstrdup(cleanup_path(sb.buf));\n        strbuf_release(&sb);\n        return path;\n\t\nwould be more sensible.\n"},{"id":"193328","messageId":"20120611185311.Horde.RALabXwdC4BP1iJ3aQ-w89A@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"7v8vft97a8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-11T16:53:11Z","receivedAt":"2012-06-11T16:53:11Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nJunio C Hamano <gitster@pobox.com> a écrit :\n\n> Erik Faye-Lund <kusmabite@gmail.com> writes:\n>\n>> The awkward thing about doing this, is that the memory allocated by\n>> the strbuf cannot be reclaimed if you go with this. A pointer that has\n>> been adjusted (like cleanup_path can do) cannot be successfully fed to\n>> free.\n>\n> Yeah, I wouldn't recommend doing that.  Either\n>\n> \tpath = strbuf_detach(&sb, NULL);\n> \tretval = xstrdup(cleanup_path(path));\n>         free(path);\n>         return retval;\n>\n> or\n>\n> \tpath = xstrdup(cleanup_path(sb.buf));\n>         strbuf_release(&sb);\n>         return path;\n>\n> would be more sensible.\n\nErik, what do you prefer ? You can have the final answer.\n"},{"id":"193402","messageId":"20120612005949.Horde.2Iy7T3wdC4BP1nhlOdxxKdA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"20120611185311.Horde.RALabXwdC4BP1iJ3aQ-w89A@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-11T22:59:49Z","receivedAt":"2012-06-11T22:59:49Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\nnguyenhu@minatec.inpg.fr a écrit :\n\n> Junio C Hamano <gitster@pobox.com> a écrit :\n>\n>> Erik Faye-Lund <kusmabite@gmail.com> writes:\n>>\n>>> The awkward thing about doing this, is that the memory allocated by\n>>> the strbuf cannot be reclaimed if you go with this. A pointer that has\n>>> been adjusted (like cleanup_path can do) cannot be successfully fed to\n>>> free.\n>>\n>> Yeah, I wouldn't recommend doing that.  Either\n>>\n>> \tpath = strbuf_detach(&sb, NULL);\n>> \tretval = xstrdup(cleanup_path(path));\n>>        free(path);\n>>        return retval;\n>>\n>> or\n>>\n>> \tpath = xstrdup(cleanup_path(sb.buf));\n>>        strbuf_release(&sb);\n>>        return path;\n>>\n>> would be more sensible.\n\nIn our next patch, mkpathdup() function will be\n\nchar *mkpathdup(const char *fmt, ...)\n{\n\tchar *path;\n\tstruct strbuf sb = STRBUF_INIT;\n\tva_list args;\n\n\tva_start(args, fmt);\n\tstrbuf_vaddf(&sb, fmt, args);\n\tva_end(args);\n\tpath = xstrdup(cleanup_path(sb.buf));\n\n\tstrbuf_release(&sb);\n\treturn path;\n}\n\nwhich looks like our previous proposal with xstrdup()\nand cleanup_path() functions at the right place.\n"},{"id":"193403","messageId":"CABPQNSZuMBUa9bHwmCRDL-dGfRd8FofBf5YsmK=nuMu1iMbnMw@mail.gmail.com","threadId":"30663","inReplyTo":"20120611185311.Horde.RALabXwdC4BP1iJ3aQ-w89A@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-11T23:03:47Z","receivedAt":"2012-06-11T23:03:47Z","isPatch":false,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Mon, Jun 11, 2012 at 6:53 PM,  <nguyenhu@minatec.inpg.fr> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> a écrit :\n>\n>> Erik Faye-Lund <kusmabite@gmail.com> writes:\n>>\n>>> The awkward thing about doing this, is that the memory allocated by\n>>> the strbuf cannot be reclaimed if you go with this. A pointer that has\n>>> been adjusted (like cleanup_path can do) cannot be successfully fed to\n>>> free.\n>>\n>>\n>> Yeah, I wouldn't recommend doing that.  Either\n>>\n>>        path = strbuf_detach(&sb, NULL);\n>>        retval = xstrdup(cleanup_path(path));\n>>        free(path);\n>>        return retval;\n>>\n>> or\n>>\n>>        path = xstrdup(cleanup_path(sb.buf));\n>>        strbuf_release(&sb);\n>>        return path;\n>>\n>> would be more sensible.\n>\n>\n> Erik, what do you prefer ? You can have the final answer.\n>\n\nI think the latter is the neater one, but I don't really care too much\neither way :)\n"},{"id":"193405","messageId":"20120612014513.Horde.cXfsRXwdC4BP1oMJ5-cA9zA@webmail.minatec.grenoble-inp.fr","threadId":"30663","inReplyTo":"vpqmx4bmhem.fsf@bauges.imag.fr","subject":"Re: [PATCHv6 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"","fromEmail":"nguyenhu@minatec.inpg.fr","sentAt":"2012-06-11T23:45:13Z","receivedAt":"2012-06-11T23:45:13Z","isPatch":false,"sender":{"key":"nguyenhu@minatec.inpg.fr","avatar":null},"body":"\n\nMatthieu Moy <Matthieu.Moy@grenoble-inp.fr> a écrit :\n\n> I don't really have opinion on what Git should do on windows, but in any\n> case, the current patches only changes the behavior if the XDG file\n> exists, so it may be suboptimal on windows, but shouldn't harm windows\n> users.\n\nYes, if the XDG file doesn't exist, there is no change, and the behaviour is\nthe same, with Linux, OSX or Windows. But it is true that, although  \nwindows users\nwill not have problems with the current patches even if they use the XDG file,\nit is not opimized for Windows, and it would be nice to optimize the case of\nWindows. For the time being, it is not the goal of this series of patches.\nAnother patch will certainly add this optimization, I hope.\n"},{"id":"193411","messageId":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1338988885-21933-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv7 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen Nguyen","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-12T02:49:53Z","receivedAt":"2012-06-12T02:49:53Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"Git will be able to read from $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git currently does not write to this new\nconfiguration file. If $XDG_CONFIG_HOME is either not set or empty,\n$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: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |   12 +++++--\n builtin/config.c             |   28 +++++++++++-----\n cache.h                      |    3 ++\n config.c                     |   23 ++++++++-----\n path.c                       |   41 ++++++++++++++++++++++++\n t/t1306-xdg-files.sh         |   70 ++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 156 insertions(+), 21 deletions(-)\n create mode 100755 t/t1306-xdg-files.sh\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex d9463cb..7e344a2 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 and 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..da54fd1 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 *global = NULL, *xdg = 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@@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)\n \n \tlocal = given_config_file;\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 (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n+\t\thome_config_paths(&global, &xdg, \"config\");\n \t}\n \n \tif (use_key_regexp) {\n@@ -229,6 +227,8 @@ 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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (do_all && global)\n \t\tgit_config_from_file(fn, global, data);\n \tif (do_all)\n@@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)\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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_)\n free_strings:\n \tfree(repo_config);\n \tfree(global);\n+\tfree(xdg);\n \treturn ret;\n }\n \n@@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \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\tchar *user_config = NULL;\n+\t\tchar *xdg_config = NULL;\n+\n+\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n+\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config;\n+\t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..fbba2d6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\n extern char *git_pathdup(const char *fmt, ...)\n \t__attribute__((format (printf, 1, 2)));\n+extern char *mkpathdup(const char *fmt, ...)\n+\t__attribute__((format (printf, 1, 2)));\n \n /* Return a statically allocated filename matching the sha1 signature */\n extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));\n@@ -708,6 +710,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern void home_config_paths(char **global, char **xdg, char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..d28a499 100644\n--- a/config.c\n+++ b/config.c\n@@ -929,7 +929,10 @@ int git_config_system(void)\n int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n {\n \tint ret = 0, found = 0;\n-\tconst char *home = NULL;\n+\tchar *xdg_config = NULL;\n+\tchar *user_config = NULL;\n+\n+\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\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\t\tfound += 1;\n-\t\t}\n+\tif (!access(xdg_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_config, data);\n+\t\tfound += 1;\n+\t}\n+\n+\tif (!access(user_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tfound += 1;\n \t}\n \n \tif (repo_config && !access(repo_config, R_OK)) {\n@@ -963,6 +966,8 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tbreak;\n \t}\n \n+\tfree(xdg_config);\n+\tfree(user_config);\n \treturn ret == 0 ? found : ret;\n }\n \ndiff --git a/path.c b/path.c\nindex 6f2aa69..66acd24 100644\n--- a/path.c\n+++ b/path.c\n@@ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...)\n \treturn xstrdup(path);\n }\n \n+char *mkpathdup(const char *fmt, ...)\n+{\n+\tchar *path;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&sb, fmt, args);\n+\tva_end(args);\n+\tpath = xstrdup(cleanup_path(sb.buf));\n+\n+\tstrbuf_release(&sb);\n+\treturn path;\n+}\n+\n char *mkpath(const char *fmt, ...)\n {\n \tva_list args;\n@@ -122,6 +137,32 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+void home_config_paths(char **global, char **xdg, char *file)\n+{\n+\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n+\tchar *home = getenv(\"HOME\");\n+\tchar *to_free = NULL;\n+\n+\tif (!home) {\n+\t\tif (global)\n+\t\t\t*global = NULL;\n+\t} else {\n+\t\tif (!xdg_home) {\n+\t\t\tto_free = mkpathdup(\"%s/.config\", home);\n+\t\t\txdg_home = to_free;\n+\t\t}\n+\t\tif (global)\n+\t\t\t*global = mkpathdup(\"%s/.gitconfig\", home);\n+\t}\n+\n+\tif (!xdg_home)\n+\t\t*xdg = NULL;\n+\telse\n+\t\t*xdg = mkpathdup(\"%s/git/%s\", xdg_home, file);\n+\n+\tfree(to_free);\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nnew file mode 100755\nindex 0000000..5b971d9\n--- /dev/null\n+++ b/t/t1306-xdg-files.sh\n@@ -0,0 +1,70 @@\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='Compatibility with $XDG_CONFIG_HOME/git/ files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[alias]\" >.config/git/config &&\n+\techo \"\tmyalias = !echo in_config\" >>.config/git/config &&\n+\techo in_config >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[alias]\" >.gitconfig &&\n+\techo \"\tmyalias = !echo in_gitconfig\" >>.gitconfig &&\n+\techo in_gitconfig >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\techo read_config >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_done\n-- \n1.7.8\n"},{"id":"193410","messageId":"1339469396-29677-2-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv7 2/4] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Huynh Khoi Nguyen Nguyen","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-12T02:49:54Z","receivedAt":"2012-06-12T02:49:54Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"To use the feature of core.excludesfile, the user needs:\n- to create such a file,\n- and add configuration variable to point at it.\nInstead, we can define a filename that is unlikely to already exist on\nthe system, and only use the presence of the file as a cue that the user\nwants to use that feature.\nWe could pick any random filename that only user can create, which\nmeans that it has to be under $HOME. Let's use\n\"${XDG_CONFIG_HOME:-$HOME/.config/git}/ignore\". It is as good as any\nrandom filename in $HOME/ and follows the XDG specification.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt    |    4 +++-\n Documentation/gitignore.txt |    4 +++-\n dir.c                       |    7 ++++++-\n t/t1306-xdg-files.sh        |   29 +++++++++++++++++++++++++++++\n 4 files changed, 41 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 915cb5a..509bf25 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -483,7 +483,9 @@ core.excludesfile::\n \t'.git/info/exclude', git looks into this file for patterns\n \tof files which are not meant to be tracked.  \"`~/`\" is expanded\n \tto the value of `$HOME` and \"`~user/`\" to the specified user's\n-\thome directory.  See linkgit:gitignore[5].\n+\thome directory. Its default value is $XDG_CONFIG_HOME/git/ignore.\n+\tIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore\n+\twill be used. See linkgit:gitignore[5].\n \n core.askpass::\n \tSome commands (e.g. svn and http interfaces) that interactively\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..a5c9b93 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into\n the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to\n ignore in all situations (e.g., backup or temporary files generated by\n the user's editor of choice) generally go into a file specified by\n-`core.excludesfile` in the user's `~/.gitconfig`.\n+`core.excludesfile` in the user's `~/.gitconfig`. Its default value is\n+$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,\n+$HOME/.config/git/ignore will be used.\n \n The underlying git plumbing tools, such as\n 'git ls-files' and 'git read-tree', read\ndiff --git a/dir.c b/dir.c\nindex ed1510f..8c6f47f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1234,12 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n void setup_standard_excludes(struct dir_struct *dir)\n {\n \tconst char *path;\n+\tchar *xdg_path;\n \n \tdir->exclude_per_dir = \".gitignore\";\n \tpath = git_path(\"info/exclude\");\n+\tif (!excludes_file) {\n+\t\thome_config_paths(NULL, &xdg_path, \"ignore\");\n+\t\texcludes_file = xdg_path;\n+\t}\n \tif (!access(path, R_OK))\n \t\tadd_excludes_from_file(dir, path);\n-\tif (excludes_file && !access(excludes_file, R_OK))\n+\tif (!access(excludes_file, R_OK))\n \t\tadd_excludes_from_file(dir, excludes_file);\n }\n \ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 5b971d9..05103f5 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -67,4 +67,33 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'\n '\n \n \n+test_expect_success 'Setup' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion of a file in the XDG ignore file' '\n+\tmkdir -p \"$HOME\"/.config/git/ &&\n+\techo to_be_excluded >\"$HOME\"/.config/git/ignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in both XDG and local ignore files' '\n+\techo to_be_excluded >.gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in a non-XDG global ignore file' '\n+\trm .gitignore &&\n+\techo >\"$HOME\"/.config/git/ignore &&\n+\techo to_be_excluded >\"$HOME\"/my_gitignore &&\n+\tgit config core.excludesfile \"$HOME\"/my_gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"193412","messageId":"1339469396-29677-3-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv7 3/4] Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes","fromName":"Huynh Khoi Nguyen Nguyen","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-12T02:49:55Z","receivedAt":"2012-06-12T02:49:55Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"To use the feature of core.attributesfile, the user needs:\n- to create such a file,\n- and add configuration variable to point at it.\nInstead, we can define a filename that is unlikely to already exist on\nthe system, and only use the presence of the file as a cue that the user\nwants to use that feature.\nWe could pick any random filename that only user can create, which\nmeans that it has to be under $HOME. Let's use\n\"${XDG_CONFIG_HOME:-$HOME/.config/git}/attributes\". It is as good as any\nrandom filename in $HOME/ and follows the XDG specification.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt        |    4 +++-\n Documentation/gitattributes.txt |    2 ++\n attr.c                          |   17 ++++++++++-------\n t/t1306-xdg-files.sh            |   29 +++++++++++++++++++++++++++++\n 4 files changed, 44 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 509bf25..f691d8e 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -500,7 +500,9 @@ core.attributesfile::\n \tIn addition to '.gitattributes' (per-directory) and\n \t'.git/info/attributes', git looks into this file for attributes\n \t(see linkgit:gitattributes[5]). Path expansions are made the same\n-\tway as for `core.excludesfile`.\n+\tway as for `core.excludesfile`. Its default value is\n+\t$XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not\n+\tset or empty, $HOME/.config/git/attributes will be used.\n \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 80120ea..484c614 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into\n `.gitattributes` files. Attributes that should affect all repositories\n for a single user should be placed in a file specified by the\n `core.attributesfile` configuration option (see linkgit:git-config[1]).\n+Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME\n+is either not set or empty, $HOME/.config/git/attributes will be used.\n Attributes for all users on a system should be placed in the\n `$(prefix)/etc/gitattributes` file.\n \ndiff --git a/attr.c b/attr.c\nindex 303751f..aef93d8 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -497,6 +497,7 @@ static int git_attr_system(void)\n static void bootstrap_attr_stack(void)\n {\n \tstruct attr_stack *elem;\n+\tchar *xdg_attributes_file;\n \n \tif (attr_stack)\n \t\treturn;\n@@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void)\n \t\t}\n \t}\n \n-\tif (git_attributes_file) {\n-\t\telem = read_attr_from_file(git_attributes_file, 1);\n-\t\tif (elem) {\n-\t\t\telem->origin = NULL;\n-\t\t\telem->prev = attr_stack;\n-\t\t\tattr_stack = elem;\n-\t\t}\n+\tif (!git_attributes_file) {\n+\t\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n+\t\tgit_attributes_file = xdg_attributes_file;\n+\t}\n+\telem = read_attr_from_file(git_attributes_file, 1);\n+\tif (elem) {\n+\t\telem->origin = NULL;\n+\t\telem->prev = attr_stack;\n+\t\tattr_stack = elem;\n \t}\n \n \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 05103f5..e8cd78a 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -96,4 +96,33 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '\n '\n \n \n+test_expect_success 'Checking attributes in the XDG attributes file' '\n+\techo foo >f &&\n+\tgit check-attr -a f >actual &&\n+\ttest_line_count -eq 0 actual &&\n+\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\techo \"f: attr_f: set\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in both XDG and local attributes files' '\n+\techo \"f -attr_f\" >.gitattributes &&\n+\techo \"f: attr_f: unset\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n+\ttest_might_fail rm .gitattributes &&\n+\techo \"f attr_f=test\" >\"$HOME\"/my_gitattributes &&\n+\tgit config core.attributesfile \"$HOME\"/my_gitattributes &&\n+\techo \"f: attr_f: test\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"193413","messageId":"1339469396-29677-4-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","threadId":"30663","inReplyTo":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"[PATCHv7 4/4] Write to $XDG_CONFIG_HOME/git/config file","fromName":"Huynh Khoi Nguyen Nguyen","fromEmail":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","sentAt":"2012-06-12T02:49:56Z","receivedAt":"2012-06-12T02:49:56Z","isPatch":false,"sender":{"key":"huynh-khoi-nguyen.nguyen@ensimag.imag.fr","avatar":null},"body":"Git will be able to write to $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification, if:\n- this file already exists, and\n- $HOME/.gitconfig file doesn't, and\n- global option is used.\nOtherwise Git writes to $HOME/.gitconfig with global option, as usual.\nIf you don't create $XDG_CONFIG_HOME/git/config, there is absolutely\nno change. Users can use this new file only if they want.\nIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config\nwill be used.\nAdvice for users who often come back to an old version of Git:\nyou shouldn't create this file.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/git-config.txt |    3 ++-\n builtin/config.c             |    5 +----\n t/t1306-xdg-files.sh         |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 33 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7e344a2..a2b4786 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -97,7 +97,8 @@ OPTIONS\n \n --global::\n \tFor writing options: write to global ~/.gitconfig file rather than\n-\tthe repository .git/config.\n+\tthe repository .git/config, write to $XDG_CONFIG_HOME/git/config file\n+\tif this file exists and ~/.gitconfig file doesn't.\n +\n For reading options: read only from global ~/.gitconfig and from\n $XDG_CONFIG_HOME/git/config rather than from all available files.\ndiff --git a/builtin/config.c b/builtin/config.c\nindex da54fd1..e8e1c0a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,10 +387,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n-\t\t    (actions == ACTION_LIST ||\n-\t\t     actions == ACTION_GET_COLOR ||\n-\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex e8cd78a..3c75c3f 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -125,4 +125,34 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n '\n \n \n+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p \"$HOME\"/.config/git &&\n+\t>\"$HOME\"/.config/git/config &&\n+\ttest_might_fail rm \"$HOME\"/.gitconfig &&\n+\tgit config --global user.name \"write_config\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_config\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.config/git/config\n+'\n+\n+\n+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '\n+\t>\"$HOME\"/.gitconfig &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.gitconfig\n+'\n+\n+\n+test_expect_success 'write: ~/.config/git/ exists and config file doesn'\\''t' '\n+\ttest_might_fail rm \"$HOME\"/.gitconfig &&\n+\ttest_might_fail rm \"$HOME\"/.config/git/config &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.gitconfig\n+'\n+\n+\n test_done\n-- \n1.7.8\n"},{"id":"193474","messageId":"4FD77D16.3060108@ramsay1.demon.co.uk","threadId":"30663","inReplyTo":"7v62b6udg1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4] Read (but not write) from XDG configuration, XDG attributes and XDG ignore files","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-06-12T17:32:06Z","receivedAt":"2012-06-12T17:32:06Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"Junio C Hamano wrote:\n> Ramsay Jones <ramsay@ramsay1.demon.co.uk> writes:\n> \n>> Subject: [PATCH] path.c: Fix a static buffer overwrite bug by avoiding mkpath()\n>>\n>> In order to fix the bug, we introduce a new variation of the mkpath()\n>> function, mkpathdup(), which avoids the use of the internal static\n>> buffers.\n> \n> Shouldn't we aim a bit higher to also avoid the use of bounded\n> buffer?  Instead of returning bad_path, retry with lengthened buffer\n> until we succeed, or something.\n> \n> Better yet, internally use strbuf_vaddf().\n\nSorry for the late reply, I've been away ...\n\nYes, I wasn't aiming too high. In fact I was only aiming for \"I spent the\nlast 20 minutes fixing up your patch so that it doesn't tickle the bug\non cygwin, and it looks like this...\" :-P\n\nWhile away, I did rewrite mkpathdup() to address your concerns (although I\ndidn't use strbuf_vaddf()).\n\nHowever, I see that Nguyen has not been idle and, with help from others, has\nfixed up the patch and re-rolled the series (v7 I think). I haven't fetched\nthat mail yet, but it looked good and should not tickle the cygwin bug. I will\ntest it soon.\n\nATB,\nRamsay Jones\n"},{"id":"193477","messageId":"4FD77F9A.6060202@ramsay1.demon.co.uk","threadId":"30663","inReplyTo":"20120608115711.Horde.-kmJPHwdC4BP0cx3RNFDX2A@webmail.minatec.grenoble-inp.fr","subject":"Re: [PATCHv6 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-06-12T17:42:50Z","receivedAt":"2012-06-12T17:42:50Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"nguyenhu@minatec.inpg.fr wrote:\n> Junio C Hamano <gitster@pobox.com> a écrit :\n> \n>>> +char *mkpathdup(const char *fmt, ...)\n>>> +{\n>>> +\tchar path[PATH_MAX];\n>>> +\tva_list args;\n>>> +\tunsigned len;\n>>> +\n>>> +\tva_start(args, fmt);\n>>> +\tlen = vsnprintf(path, sizeof(path), fmt, args);\n>>> +\tva_end(args);\n>>> +\tif (len >= sizeof(path))\n>>> +\t\treturn xstrdup(bad_path);\n>>> +\treturn xstrdup(cleanup_path(path));\n>>> +}\n>> Hrmph. If a new helper is introduced anyway, wouldn't it be a better\n>> idea to get rid of the hardcoded PATH_MAX limitation, perhaps using\n>> strbuf_vaddf() or something in the implementation of this function?\n> \n> Ramsay Jones, what do you think about this ?\n\nI think that I'm sorry for the late reply, I've been away ...  :-D\n\nI noticed your new series (v7 I think) which looked good (as far as the\nmkpathdup() implementation is concerned) and I don't think it will tickle\nthe cygwin bug. (I haven't actually fetched that mail yet, I've only read\nit using my ISPs web-mail interface, but will do so soon and test it on\ncygwin).\n\nThanks!\n\nATB,\nRamsay Jones\n"},{"id":"193666","messageId":"4FDA200A.8080200@ramsay1.demon.co.uk","threadId":"30663","inReplyTo":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv7 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsay1.demon.co.uk","sentAt":"2012-06-14T17:31:54Z","receivedAt":"2012-06-14T17:31:54Z","isPatch":false,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"Huynh Khoi Nguyen Nguyen wrote:\n> Git will be able to read from $XDG_CONFIG_HOME/git/config, a new\n> configuration file following XDG specification. In the order of\n> reading, this file is between global configuration file and system\n> wide configuration file. Git currently does not write to this new\n> configuration file. If $XDG_CONFIG_HOME is either not set or empty,\n> $HOME/.config/git/config will be used.\n> \n> Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> Signed-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\n> Signed-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\n> Signed-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\n> Signed-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n> ---\n>  Documentation/git-config.txt |   12 +++++--\n>  builtin/config.c             |   28 +++++++++++-----\n>  cache.h                      |    3 ++\n>  config.c                     |   23 ++++++++-----\n>  path.c                       |   41 ++++++++++++++++++++++++\n>  t/t1306-xdg-files.sh         |   70 ++++++++++++++++++++++++++++++++++++++++++\n>  6 files changed, 156 insertions(+), 21 deletions(-)\n>  create mode 100755 t/t1306-xdg-files.sh\n\n[...]\n\n> diff --git a/path.c b/path.c\n> index 6f2aa69..66acd24 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...)\n>  \treturn xstrdup(path);\n>  }\n>  \n> +char *mkpathdup(const char *fmt, ...)\n> +{\n> +\tchar *path;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\tva_list args;\n> +\n> +\tva_start(args, fmt);\n> +\tstrbuf_vaddf(&sb, fmt, args);\n> +\tva_end(args);\n> +\tpath = xstrdup(cleanup_path(sb.buf));\n> +\n> +\tstrbuf_release(&sb);\n> +\treturn path;\n> +}\n> +\n\nAs expected, this version avoids re-introducing the bug on Cygwin.\n\nI tested the series on Cygwin, MinGW and Linux and it passes it's\nown tests (t1306-xdg-files.sh) on all platforms. (Well, on MinGW\nI had to run it thus:\n\n    $ GIT_TEST_CMP='diff -ub' ./t1306-xdg-files.sh\n\notherwise the first two tests fail because of CRLF vs LF issues).\n\nThanks!\n\nATB,\nRamsay Jones\n"},{"id":"194040","messageId":"vpqhau4sixw.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"1339469396-29677-1-git-send-email-Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv7 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-21T16:55:55Z","receivedAt":"2012-06-21T16:55:55Z","isPatch":false,"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> Git will be able to read from $XDG_CONFIG_HOME/git/config, a new\n> configuration file following XDG specification. In the order of\n> reading, this file is between global configuration file and system\n> wide configuration file. Git currently does not write to this new\n> configuration file. If $XDG_CONFIG_HOME is either not set or empty,\n> $HOME/.config/git/config will be used.\n\nWhat happened to this serie? I think all comments have been addressed,\nbut I don't see it in pu... Should I resend and Cc Junio ?\n\nThanks,\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"194044","messageId":"7vfw9oshpz.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpqhau4sixw.fsf@bauges.imag.fr","subject":"Re: [PATCHv7 1/4] Read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-21T17:22:16Z","receivedAt":"2012-06-21T17:22:16Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n> writes:\n>\n>> Git will be able to read from $XDG_CONFIG_HOME/git/config, a new\n>> configuration file following XDG specification. In the order of\n>> reading, this file is between global configuration file and system\n>> wide configuration file. Git currently does not write to this new\n>> configuration file. If $XDG_CONFIG_HOME is either not set or empty,\n>> $HOME/.config/git/config will be used.\n>\n> What happened to this serie? I think all comments have been addressed,\n> but I don't see it in pu... Should I resend and Cc Junio ?\n\nIf it is ready for testing, please do.  I wasn't interested and was\nnot paying attention during the feature freeze.\n"},{"id":"194057","messageId":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"7vfw9oshpz.fsf@alter.siamese.dyndns.org","subject":"[PATCH 0/4 v8] Git configuration directory","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-06-22T09:03:22Z","receivedAt":"2012-06-22T09:03:22Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"This is a resend of a serie already discussed here:\n\n  http://thread.gmane.org/gmane.comp.version-control.git/198837\n\nI took the liberty of rewording the commit messages, and change a few\nwords in the documentation (changed some \"will be used\" to \"is used\ninstead\", which sounded more consistant with the context).\n\nA reminder of the context and objectives of the serie:\n\nGit currently stores its configuration file in ~/.gitconfig, which is\nnice and customary on Unix, as long as one has only one configuration\nfile.\n\nBut a typical user may want to store not only the config file, but\nalso the files pointed to by core.excludesfile and\ncore.attributesfile, which currently have no default values. To store\nseveral configuration files, it makes sense to have a configuration\n_directory_ instead of a set of configuration files, all right into\n$HOME. Calling this configuration directory ~/.git or ~/.gitconfig is\nnot an option, since these paths already have another meaning. Using\nthe XDG specification (in short: ~/.config/git) doesn't have this\ndrawback, and allows the user to store his configuration files right\nnext to files from other applications following the XDG standard.\n\nThe first 3 patches add read support for this \"configuration\ndirectory\", and the last one gives opt-in write support, allowing\nusers to make this \"configuration directory\" their way of life, and\nnever hear again about ~/.gitconfig if (and only if) they do not want to.\n\nNone of the patches change the behavior for people who do not create\nthe new configuration files.\n\nHuynh Khoi Nguyen Nguyen (4):\n  config: read (but not write) from $XDG_CONFIG_HOME/git/config file\n  Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore\n  Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore\n  config: write to $XDG_CONFIG_HOME/git/config file if appropriate\n\n Documentation/config.txt        |   8 +-\n Documentation/git-config.txt    |  15 +++-\n Documentation/gitattributes.txt |   2 +\n Documentation/gitignore.txt     |   4 +-\n attr.c                          |  17 +++--\n builtin/config.c                |  25 ++++---\n cache.h                         |   3 +\n config.c                        |  23 +++---\n dir.c                           |   7 +-\n path.c                          |  41 +++++++++++\n t/t1306-xdg-files.sh            | 158 ++++++++++++++++++++++++++++++++++++++++\n 11 files changed, 270 insertions(+), 33 deletions(-)\n create mode 100755 t/t1306-xdg-files.sh\n\n-- \n1.7.11.rc3.235.gd0d1d08\n"},{"id":"194060","messageId":"1340355806-6894-2-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 1/4 v8] config: read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-06-22T09:03:23Z","receivedAt":"2012-06-22T09:03:23Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n\nGit will be able to read from $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification. In the order of\nreading, this file is between global configuration file and system\nwide configuration file. Git currently does not write to this new\nconfiguration file. If $XDG_CONFIG_HOME is either not set or empty,\n$HOME/.config/git/config will be used. If the new file does not exist,\nthe behavior is unchanged.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/git-config.txt | 12 ++++++--\n builtin/config.c             | 28 ++++++++++++------\n cache.h                      |  3 ++\n config.c                     | 23 +++++++++------\n path.c                       | 41 ++++++++++++++++++++++++++\n t/t1306-xdg-files.sh         | 70 ++++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 156 insertions(+), 21 deletions(-)\n create mode 100755 t/t1306-xdg-files.sh\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex d9463cb..7e344a2 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 and 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,12 @@ $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. If $XDG_CONFIG_HOME is not set\n+\tor empty, $HOME/.config/git/config will be used. Any single-valued\n+\tvariable set in this file will be overwritten by whatever is in\n+\t~/.gitconfig.\n+\n $(prefix)/etc/gitconfig::\n \tSystem-wide configuration file.\n \ndiff --git a/builtin/config.c b/builtin/config.c\nindex 33c8820..da54fd1 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 *global = NULL, *xdg = 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@@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_)\n \n \tlocal = given_config_file;\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 (git_config_system())\n \t\t\tsystem_wide = git_etc_gitconfig();\n+\t\thome_config_paths(&global, &xdg, \"config\");\n \t}\n \n \tif (use_key_regexp) {\n@@ -229,6 +227,8 @@ 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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (do_all && global)\n \t\tgit_config_from_file(fn, global, data);\n \tif (do_all)\n@@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_)\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 && xdg)\n+\t\tgit_config_from_file(fn, xdg, data);\n \tif (!do_all && !seen && system_wide)\n \t\tgit_config_from_file(fn, system_wide, data);\n \n@@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_)\n free_strings:\n \tfree(repo_config);\n \tfree(global);\n+\tfree(xdg);\n \treturn ret;\n }\n \n@@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t}\n \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\tchar *user_config = NULL;\n+\t\tchar *xdg_config = NULL;\n+\n+\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n+\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n+\t\t    (actions == ACTION_LIST ||\n+\t\t     actions == ACTION_GET_COLOR ||\n+\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\t\tgiven_config_file = xdg_config;\n+\t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n-\t\t} else {\n+\t\telse\n \t\t\tdie(\"$HOME not set\");\n-\t\t}\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/cache.h b/cache.h\nindex 06413e1..fbba2d6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -619,6 +619,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)\n \t__attribute__((format (printf, 3, 4)));\n extern char *git_pathdup(const char *fmt, ...)\n \t__attribute__((format (printf, 1, 2)));\n+extern char *mkpathdup(const char *fmt, ...)\n+\t__attribute__((format (printf, 1, 2)));\n \n /* Return a statically allocated filename matching the sha1 signature */\n extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));\n@@ -708,6 +710,7 @@ int set_shared_perm(const char *path, int mode);\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n int mkdir_in_gitdir(const char *path);\n+extern void home_config_paths(char **global, char **xdg, char *file);\n extern char *expand_user_path(const char *path);\n const char *enter_repo(const char *path, int strict);\n static inline int is_absolute_path(const char *path)\ndiff --git a/config.c b/config.c\nindex 71ef171..d28a499 100644\n--- a/config.c\n+++ b/config.c\n@@ -929,7 +929,10 @@ int git_config_system(void)\n int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n {\n \tint ret = 0, found = 0;\n-\tconst char *home = NULL;\n+\tchar *xdg_config = NULL;\n+\tchar *user_config = NULL;\n+\n+\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n@@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\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\t\tfound += 1;\n-\t\t}\n+\tif (!access(xdg_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, xdg_config, data);\n+\t\tfound += 1;\n+\t}\n+\n+\tif (!access(user_config, R_OK)) {\n+\t\tret += git_config_from_file(fn, user_config, data);\n+\t\tfound += 1;\n \t}\n \n \tif (repo_config && !access(repo_config, R_OK)) {\n@@ -963,6 +966,8 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tbreak;\n \t}\n \n+\tfree(xdg_config);\n+\tfree(user_config);\n \treturn ret == 0 ? found : ret;\n }\n \ndiff --git a/path.c b/path.c\nindex 6f2aa69..66acd24 100644\n--- a/path.c\n+++ b/path.c\n@@ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...)\n \treturn xstrdup(path);\n }\n \n+char *mkpathdup(const char *fmt, ...)\n+{\n+\tchar *path;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tva_list args;\n+\n+\tva_start(args, fmt);\n+\tstrbuf_vaddf(&sb, fmt, args);\n+\tva_end(args);\n+\tpath = xstrdup(cleanup_path(sb.buf));\n+\n+\tstrbuf_release(&sb);\n+\treturn path;\n+}\n+\n char *mkpath(const char *fmt, ...)\n {\n \tva_list args;\n@@ -122,6 +137,32 @@ char *git_path(const char *fmt, ...)\n \treturn cleanup_path(pathname);\n }\n \n+void home_config_paths(char **global, char **xdg, char *file)\n+{\n+\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n+\tchar *home = getenv(\"HOME\");\n+\tchar *to_free = NULL;\n+\n+\tif (!home) {\n+\t\tif (global)\n+\t\t\t*global = NULL;\n+\t} else {\n+\t\tif (!xdg_home) {\n+\t\t\tto_free = mkpathdup(\"%s/.config\", home);\n+\t\t\txdg_home = to_free;\n+\t\t}\n+\t\tif (global)\n+\t\t\t*global = mkpathdup(\"%s/.gitconfig\", home);\n+\t}\n+\n+\tif (!xdg_home)\n+\t\t*xdg = NULL;\n+\telse\n+\t\t*xdg = mkpathdup(\"%s/git/%s\", xdg_home, file);\n+\n+\tfree(to_free);\n+}\n+\n char *git_path_submodule(const char *path, const char *fmt, ...)\n {\n \tchar *pathname = get_pathname();\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nnew file mode 100755\nindex 0000000..5b971d9\n--- /dev/null\n+++ b/t/t1306-xdg-files.sh\n@@ -0,0 +1,70 @@\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='Compatibility with $XDG_CONFIG_HOME/git/ files'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p .config/git &&\n+\techo \"[alias]\" >.config/git/config &&\n+\techo \"\tmyalias = !echo in_config\" >>.config/git/config &&\n+\techo in_config >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' '\n+\t>.gitconfig &&\n+\techo \"[alias]\" >.gitconfig &&\n+\techo \"\tmyalias = !echo in_gitconfig\" >>.gitconfig &&\n+\techo in_gitconfig >expected &&\n+\tgit myalias >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\trm .gitconfig &&\n+\techo \"[user]\" >.config/git/config &&\n+\techo \"\tname = read_config\" >>.config/git/config &&\n+\techo read_config >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --get user.name >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\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 >expected &&\n+\tgit config --global --list >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_done\n-- \n1.7.11.rc3.235.gd0d1d08\n"},{"id":"194058","messageId":"1340355806-6894-3-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 2/4 v8] Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-06-22T09:03:24Z","receivedAt":"2012-06-22T09:03:24Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n\nTo use the feature of core.excludesfile, the user needs:\n\n 1. to create such a file,\n\n 2. and add configuration variable to point at it.\n\nInstead, we can make this a one-step process by choosing a default value\nwhich points to a filename in the user's $HOME, that is unlikely to\nalready exist on the system, and only use the presence of the file as a\ncue that the user wants to use that feature.\n\nWe use \"${XDG_CONFIG_HOME:-$HOME/.config/git}/ignore\" as a default value.\nIt goes in the same directory as the newly added configuration\nfile (\"${XDG_CONFIG_HOME:-$HOME/.config/git}/config), and follows the XDG\nspecification.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/config.txt    |  4 +++-\n Documentation/gitignore.txt |  4 +++-\n dir.c                       |  7 ++++++-\n t/t1306-xdg-files.sh        | 29 +++++++++++++++++++++++++++++\n 4 files changed, 41 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 915cb5a..20e9531 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -483,7 +483,9 @@ core.excludesfile::\n \t'.git/info/exclude', git looks into this file for patterns\n \tof files which are not meant to be tracked.  \"`~/`\" is expanded\n \tto the value of `$HOME` and \"`~user/`\" to the specified user's\n-\thome directory.  See linkgit:gitignore[5].\n+\thome directory. Its default value is $XDG_CONFIG_HOME/git/ignore.\n+\tIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore\n+\tis used instead. See linkgit:gitignore[5].\n \n core.askpass::\n \tSome commands (e.g. svn and http interfaces) that interactively\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 2e7328b..c1f692a 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into\n the `$GIT_DIR/info/exclude` file.  Patterns which a user wants git to\n ignore in all situations (e.g., backup or temporary files generated by\n the user's editor of choice) generally go into a file specified by\n-`core.excludesfile` in the user's `~/.gitconfig`.\n+`core.excludesfile` in the user's `~/.gitconfig`. Its default value is\n+$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty,\n+$HOME/.config/git/ignore is used instead.\n \n The underlying git plumbing tools, such as\n 'git ls-files' and 'git read-tree', read\ndiff --git a/dir.c b/dir.c\nindex ed1510f..8c6f47f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -1234,12 +1234,17 @@ int remove_dir_recursively(struct strbuf *path, int flag)\n void setup_standard_excludes(struct dir_struct *dir)\n {\n \tconst char *path;\n+\tchar *xdg_path;\n \n \tdir->exclude_per_dir = \".gitignore\";\n \tpath = git_path(\"info/exclude\");\n+\tif (!excludes_file) {\n+\t\thome_config_paths(NULL, &xdg_path, \"ignore\");\n+\t\texcludes_file = xdg_path;\n+\t}\n \tif (!access(path, R_OK))\n \t\tadd_excludes_from_file(dir, path);\n-\tif (excludes_file && !access(excludes_file, R_OK))\n+\tif (!access(excludes_file, R_OK))\n \t\tadd_excludes_from_file(dir, excludes_file);\n }\n \ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 5b971d9..05103f5 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -67,4 +67,33 @@ test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists'\n '\n \n \n+test_expect_success 'Setup' '\n+\tgit init git &&\n+\tcd git &&\n+\techo foo >to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion of a file in the XDG ignore file' '\n+\tmkdir -p \"$HOME\"/.config/git/ &&\n+\techo to_be_excluded >\"$HOME\"/.config/git/ignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in both XDG and local ignore files' '\n+\techo to_be_excluded >.gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n+test_expect_success 'Exclusion in a non-XDG global ignore file' '\n+\trm .gitignore &&\n+\techo >\"$HOME\"/.config/git/ignore &&\n+\techo to_be_excluded >\"$HOME\"/my_gitignore &&\n+\tgit config core.excludesfile \"$HOME\"/my_gitignore &&\n+\ttest_must_fail git add to_be_excluded\n+'\n+\n+\n test_done\n-- \n1.7.11.rc3.235.gd0d1d08\n"},{"id":"194061","messageId":"1340355806-6894-4-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 3/4 v8] Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-06-22T09:03:25Z","receivedAt":"2012-06-22T09:03:25Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n\nTo use the feature of core.attributesfile, the user needs:\n\n 1. to create such a file,\n\n 2. and add configuration variable to point at it.\n\nInstead, we can make this a one-step process by choosing a default value\nwhich points to a filename in the user's $HOME, that is unlikely to\nalready exist on the system, and only use the presence of the file as a\ncue that the user wants to use that feature.\n\nWe use \"${XDG_CONFIG_HOME:-$HOME/.config/git}/ignore\" as a default value.\nIt goes in the same directory as the newly added configuration\nfile (\"${XDG_CONFIG_HOME:-$HOME/.config/git}/config), and follows the XDG\nspecification.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n\nThe commit message is amlost identical to the previous one to make the\npatch self-contained, but this can be changed to something shorter\nlike \"like the previous patch, we set the default avlue ...\" if needed.\n\n Documentation/config.txt        |  4 +++-\n Documentation/gitattributes.txt |  2 ++\n attr.c                          | 17 ++++++++++-------\n t/t1306-xdg-files.sh            | 29 +++++++++++++++++++++++++++++\n 4 files changed, 44 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 20e9531..db07459 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -500,7 +500,9 @@ core.attributesfile::\n \tIn addition to '.gitattributes' (per-directory) and\n \t'.git/info/attributes', git looks into this file for attributes\n \t(see linkgit:gitattributes[5]). Path expansions are made the same\n-\tway as for `core.excludesfile`.\n+\tway as for `core.excludesfile`. Its default value is\n+\t$XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not\n+\tset or empty, $HOME/.config/git/attributes is used instead.\n \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex 80120ea..e16f3e1 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into\n `.gitattributes` files. Attributes that should affect all repositories\n for a single user should be placed in a file specified by the\n `core.attributesfile` configuration option (see linkgit:git-config[1]).\n+Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME\n+is either not set or empty, $HOME/.config/git/attributes is used instead.\n Attributes for all users on a system should be placed in the\n `$(prefix)/etc/gitattributes` file.\n \ndiff --git a/attr.c b/attr.c\nindex 303751f..aef93d8 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -497,6 +497,7 @@ static int git_attr_system(void)\n static void bootstrap_attr_stack(void)\n {\n \tstruct attr_stack *elem;\n+\tchar *xdg_attributes_file;\n \n \tif (attr_stack)\n \t\treturn;\n@@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void)\n \t\t}\n \t}\n \n-\tif (git_attributes_file) {\n-\t\telem = read_attr_from_file(git_attributes_file, 1);\n-\t\tif (elem) {\n-\t\t\telem->origin = NULL;\n-\t\t\telem->prev = attr_stack;\n-\t\t\tattr_stack = elem;\n-\t\t}\n+\tif (!git_attributes_file) {\n+\t\thome_config_paths(NULL, &xdg_attributes_file, \"attributes\");\n+\t\tgit_attributes_file = xdg_attributes_file;\n+\t}\n+\telem = read_attr_from_file(git_attributes_file, 1);\n+\tif (elem) {\n+\t\telem->origin = NULL;\n+\t\telem->prev = attr_stack;\n+\t\tattr_stack = elem;\n \t}\n \n \tif (!is_bare_repository() || direction == GIT_ATTR_INDEX) {\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 05103f5..e8cd78a 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -96,4 +96,33 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '\n '\n \n \n+test_expect_success 'Checking attributes in the XDG attributes file' '\n+\techo foo >f &&\n+\tgit check-attr -a f >actual &&\n+\ttest_line_count -eq 0 actual &&\n+\techo \"f attr_f\" >\"$HOME\"/.config/git/attributes &&\n+\techo \"f: attr_f: set\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in both XDG and local attributes files' '\n+\techo \"f -attr_f\" >.gitattributes &&\n+\techo \"f: attr_f: unset\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n+test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n+\ttest_might_fail rm .gitattributes &&\n+\techo \"f attr_f=test\" >\"$HOME\"/my_gitattributes &&\n+\tgit config core.attributesfile \"$HOME\"/my_gitattributes &&\n+\techo \"f: attr_f: test\" >expected &&\n+\tgit check-attr -a f >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+\n test_done\n-- \n1.7.11.rc3.235.gd0d1d08\n"},{"id":"194059","messageId":"1340355806-6894-5-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 4/4 v8] config: write to $XDG_CONFIG_HOME/git/config file if appropriate","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-06-22T09:03:26Z","receivedAt":"2012-06-22T09:03:26Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n\nGit will be able to write to $XDG_CONFIG_HOME/git/config, a new\nconfiguration file following XDG specification, if:\n\n 1. this file already exists, and\n\n 2. $HOME/.gitconfig file doesn't, and\n\n 3. The --global option is used.\n\nOtherwise Git writes to $HOME/.gitconfig with the --global option, as\nusual. If the user doesn't create $XDG_CONFIG_HOME/git/config, there is\nabsolutely no change. Users can use this new file only if they want.\n\nIf $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/config\nwill be used.\n\nAdvice for users who often come back to an old version of Git: you\nshouldn't create this file.\n\nSigned-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\nSigned-off-by: Valentin Duperray <Valentin.Duperray@ensimag.imag.fr>\nSigned-off-by: Franck Jonas <Franck.Jonas@ensimag.imag.fr>\nSigned-off-by: Lucien Kong <Lucien.Kong@ensimag.imag.fr>\nSigned-off-by: Thomas Nguy <Thomas.Nguy@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/git-config.txt |  3 ++-\n builtin/config.c             |  5 +----\n t/t1306-xdg-files.sh         | 30 ++++++++++++++++++++++++++++++\n 3 files changed, 33 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex 7e344a2..fb10082 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -97,7 +97,8 @@ OPTIONS\n \n --global::\n \tFor writing options: write to global ~/.gitconfig file rather than\n-\tthe repository .git/config.\n+\tthe repository .git/config, write to $XDG_CONFIG_HOME/git/config file\n+\tif this file exists and the ~/.gitconfig file doesn't.\n +\n For reading options: read only from global ~/.gitconfig and from\n $XDG_CONFIG_HOME/git/config rather than from all available files.\ndiff --git a/builtin/config.c b/builtin/config.c\nindex da54fd1..e8e1c0a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,10 +387,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n-\t\t    (actions == ACTION_LIST ||\n-\t\t     actions == ACTION_GET_COLOR ||\n-\t\t     actions == ACTION_GET_COLORBOOL))\n+\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex e8cd78a..3c75c3f 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -125,4 +125,34 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '\n '\n \n \n+test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\\''t' '\n+\tmkdir -p \"$HOME\"/.config/git &&\n+\t>\"$HOME\"/.config/git/config &&\n+\ttest_might_fail rm \"$HOME\"/.gitconfig &&\n+\tgit config --global user.name \"write_config\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_config\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.config/git/config\n+'\n+\n+\n+test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '\n+\t>\"$HOME\"/.gitconfig &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.gitconfig\n+'\n+\n+\n+test_expect_success 'write: ~/.config/git/ exists and config file doesn'\\''t' '\n+\ttest_might_fail rm \"$HOME\"/.gitconfig &&\n+\ttest_might_fail rm \"$HOME\"/.config/git/config &&\n+\tgit config --global user.name \"write_gitconfig\" &&\n+\techo \"[user]\" >expected &&\n+\techo \"\tname = write_gitconfig\" >>expected &&\n+\ttest_cmp expected \"$HOME\"/.gitconfig\n+'\n+\n+\n test_done\n-- \n1.7.11.rc3.235.gd0d1d08\n"},{"id":"194102","messageId":"7vbokbnixy.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1340355806-6894-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 0/4 v8] Git configuration directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-22T21:19:21Z","receivedAt":"2012-06-22T21:19:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> I took the liberty of rewording the commit messages, and change a few\n> words in the documentation (changed some \"will be used\" to \"is used\n> instead\", which sounded more consistant with the context).\n\nThanks; the series overall looks good.\n\nI may have made further tweaks by the time I push the result\nout.  Please watch 'pu' and holler if you find anything objectionable.\n\n\n\n> A reminder of the context and objectives of the serie:\n>\n> Git currently stores its configuration file in ~/.gitconfig, which is\n> nice and customary on Unix, as long as one has only one configuration\n> file.\n>\n> But a typical user may want to store not only the config file, but\n> also the files pointed to by core.excludesfile and\n> core.attributesfile, which currently have no default values. To store\n> several configuration files, it makes sense to have a configuration\n> _directory_ instead of a set of configuration files, all right into\n> $HOME. Calling this configuration directory ~/.git or ~/.gitconfig is\n> not an option, since these paths already have another meaning. Using\n> the XDG specification (in short: ~/.config/git) doesn't have this\n> drawback, and allows the user to store his configuration files right\n> next to files from other applications following the XDG standard.\n>\n> The first 3 patches add read support for this \"configuration\n> directory\", and the last one gives opt-in write support, allowing\n> users to make this \"configuration directory\" their way of life, and\n> never hear again about ~/.gitconfig if (and only if) they do not want to.\n>\n> None of the patches change the behavior for people who do not create\n> the new configuration files.\n>\n> Huynh Khoi Nguyen Nguyen (4):\n>   config: read (but not write) from $XDG_CONFIG_HOME/git/config file\n>   Let core.excludesfile default to $XDG_CONFIG_HOME/git/ignore\n>   Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore\n>   config: write to $XDG_CONFIG_HOME/git/config file if appropriate\n>\n>  Documentation/config.txt        |   8 +-\n>  Documentation/git-config.txt    |  15 +++-\n>  Documentation/gitattributes.txt |   2 +\n>  Documentation/gitignore.txt     |   4 +-\n>  attr.c                          |  17 +++--\n>  builtin/config.c                |  25 ++++---\n>  cache.h                         |   3 +\n>  config.c                        |  23 +++---\n>  dir.c                           |   7 +-\n>  path.c                          |  41 +++++++++++\n>  t/t1306-xdg-files.sh            | 158 ++++++++++++++++++++++++++++++++++++++++\n>  11 files changed, 270 insertions(+), 33 deletions(-)\n>  create mode 100755 t/t1306-xdg-files.sh\n"},{"id":"194103","messageId":"7v4nq3niwc.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1340355806-6894-4-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 3/4 v8] Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-22T21:20:19Z","receivedAt":"2012-06-22T21:20:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n>\n> To use the feature of core.attributesfile, the user needs:\n>\n>  1. to create such a file,\n>\n>  2. and add configuration variable to point at it.\n>\n> Instead, we can make this a one-step process by choosing a default value\n> which points to a filename in the user's $HOME, that is unlikely to\n> already exist on the system, and only use the presence of the file as a\n> cue that the user wants to use that feature.\n>\n> We use \"${XDG_CONFIG_HOME:-$HOME/.config/git}/ignore\" as a default value.\n> It goes in the same directory as the newly added configuration\n> file (\"${XDG_CONFIG_HOME:-$HOME/.config/git}/config), and follows the XDG\n> specification.\n\nThe patch text may be OK but look at the Subject: line and notice\nsomething funny ;-)\n\nGiven the root cause of that \"something funny\", I'll reduce the log\nmessage of this one down to:\n\n    Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes\n    \n    This gives the default value for the core.attributesfile variable\n    following the exact same logic of the previous change for the\n    core.excludesfile setting.\n    \n    Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n    ...\n    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\nI am not sure about use of git/ignore in [2/2], though.\n\nShouldn't the default value for core.excludesfile be git/exclude,\nnot git/ignore?\n"},{"id":"194104","messageId":"7vwr2zm4bm.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1340355806-6894-5-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 4/4 v8] config: write to $XDG_CONFIG_HOME/git/config file if appropriate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-22T21:20:29Z","receivedAt":"2012-06-22T21:20:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n>\n> Git will be able to write to $XDG_CONFIG_HOME/git/config, a new\n> ...\n> Advice for users who often come back to an old version of Git: you\n> shouldn't create this file.\n\nHrm, is there a better way to give this advice to the _users_?  The\nlog message is primarily for people who need to dig the development\nhistory of Git, and they are not the people who will be hurt by this\nchange.  Perhaps the change to Documentation/git-config.txt in this\npatch needs to be a bit more detailed?\n\nIdeally, it would be nice if we could do the usual \"advise\" thing\nby detecting a possible confusion, but I do not offhand see a clever\nway to do so.\n"},{"id":"194183","messageId":"vpqehp3j403.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7v4nq3niwc.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/4 v8] Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-25T06:32:28Z","receivedAt":"2012-06-25T06:32:28Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> The patch text may be OK but look at the Subject: line and notice\n> something funny ;-)\n\nNice catch.\n\n> Given the root cause of that \"something funny\", I'll reduce the log\n> message of this one down to:\n>\n>     Let core.attributesfile default to $XDG_CONFIG_HOME/git/attributes\n>     \n>     This gives the default value for the core.attributesfile variable\n>     following the exact same logic of the previous change for the\n>     core.excludesfile setting.\n>     \n>     Signed-off-by: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n>     ...\n>     Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\nGood (I see you did the change yourself in pu).\n\n> I am not sure about use of git/ignore in [2/2], though.\n>\n> Shouldn't the default value for core.excludesfile be git/exclude,\n> not git/ignore?\n\nBoth are valid options, and unfortunately, Git currently use both\n\"ignore\" and \"exclude\" to mean the same thing. \n\nI have a slight preference for \"ignore\", because this file is the\nuser-wide version of the .gitignore file. I think most non-advanced\nusers have already been exposed to the wording \"ignore\" with this file,\nbut much less have been exposed to \"exclude\" (for example, I don't\nremember having ever used the per-directory .git/info/exclude). So,\ngoing for \"ignore\" means that the wording used for the most common\noperations will be consistant, and only relatively advanced users (who\nuse .git/info/exclude, or \"git ls-files --exclude-standard\", or need to\nset core.excludesfile to some value other than the default) will need to\nlearn that \"exclude\" is synonym for \"ignore\" in the Git world.\n\nActually, it would even make sense to reduce the number of occurences of\n\"excludes\" in the UI, e.g. support something like core.ignoresfile as an\nalias for core.excludesfile, along the lines of:\n\n--- a/config.c\n+++ b/config.c\n@@ -723,7 +723,7 @@ static int git_default_core_config(const char *var, const char *value)\n        if (!strcmp(var, \"core.askpass\"))\n                return git_config_string(&askpass_program, var, value);\n \n-       if (!strcmp(var, \"core.excludesfile\"))\n+       if (!strcmp(var, \"core.excludesfile\") || !strcmp(var, \"core.ignoresfile\"))\n                return git_config_pathname(&excludes_file, var, value);\n \n        if (!strcmp(var, \"core.whitespace\")) {\n\n(which would off course require some documentation)\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"194184","messageId":"vpq395jj3dt.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vwr2zm4bm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/4 v8] config: write to $XDG_CONFIG_HOME/git/config file if appropriate","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-25T06:45:50Z","receivedAt":"2012-06-25T06:45:50Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n>\n>> From: Huynh Khoi Nguyen Nguyen <Huynh-Khoi-Nguyen.Nguyen@ensimag.imag.fr>\n>>\n>> Git will be able to write to $XDG_CONFIG_HOME/git/config, a new\n>> ...\n>> Advice for users who often come back to an old version of Git: you\n>> shouldn't create this file.\n>\n> Hrm, is there a better way to give this advice to the _users_?  The\n> log message is primarily for people who need to dig the development\n> history of Git, and they are not the people who will be hurt by this\n> change.  Perhaps the change to Documentation/git-config.txt in this\n> patch needs to be a bit more detailed?\n\nWe can squash this into PATCH 1:\n\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -209,7 +209,9 @@ $XDG_CONFIG_HOME/git/config::\n        Second user-specific configuration file. If $XDG_CONFIG_HOME is not set\n        or empty, $HOME/.config/git/config will be used. Any single-valued\n        variable set in this file will be overwritten by whatever is in\n-       ~/.gitconfig.\n+       ~/.gitconfig. Support for this file was added after Git\n+       1.7.11, so if you sometimes use older versions of Git, it's a\n+       good idea not to create this file.\n \n $(prefix)/etc/gitconfig::\n        System-wide configuration file.\n\n(If your cristal ball tells you what version number will follow 1.7.11\n(1.7.12 or 1.8.0?), we can change \"after 1.7.11\" to \"in X\". Do you want\nto squash it yourself, or shall I resend?\n\n> Ideally, it would be nice if we could do the usual \"advise\" thing\n> by detecting a possible confusion, but I do not offhand see a clever\n> way to do so.\n\nI don't see either, since the advice should be given only to people who\nuse various versions of Git.\n\nAnyway, I don't think we should bother too much with this: it's really\nan opt-in feature, Git will never create the new file for you (it will\nonly write in it if it exists). So, people potentially harmed by this\nchange are only those who read the documentation. If the documentation\nwarns them, I think it's sufficient. It's not very different from any\nnew feature actually.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"194185","messageId":"7vlijbhn3x.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpqehp3j403.fsf@bauges.imag.fr","subject":"Re: [PATCH 3/4 v8] Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-25T07:22:42Z","receivedAt":"2012-06-25T07:22:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Actually, it would even make sense to reduce the number of occurences of\n> \"excludes\" in the UI, e.g. support something like core.ignoresfile as an\n> alias for core.excludesfile, along the lines of:\n>\n> --- a/config.c\n> +++ b/config.c\n> @@ -723,7 +723,7 @@ static int git_default_core_config(const char *var, const char *value)\n>         if (!strcmp(var, \"core.askpass\"))\n>                 return git_config_string(&askpass_program, var, value);\n>  \n> -       if (!strcmp(var, \"core.excludesfile\"))\n> +       if (!strcmp(var, \"core.excludesfile\") || !strcmp(var, \"core.ignoresfile\"))\n>                 return git_config_pathname(&excludes_file, var, value);\n\nI somehow suspect this is going backwards.  \".gitignore\" may have\nbeen the original mistake; it is much more than \".cvsignore\".\n\nBut I do not deeply care either way.  It just felt $path/git/ignore\nwas out of place given $path/.git/info/exclude has been with us\nalmost forever.\n"},{"id":"194187","messageId":"vpqd34nhljj.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vlijbhn3x.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/4 v8] Let core.attributesfile default to $XDG_CONFIG_HOME/git/ignore","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-25T07:56:32Z","receivedAt":"2012-06-25T07:56:32Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I somehow suspect this is going backwards.  \".gitignore\" may have\n> been the original mistake; it is much more than \".cvsignore\".\n\nI don't think they are _that_ different, and if we're talking about how\nother VCS call this, I can think of \".hgignore\", \".bzrignore\", ...\n\nEven within the Git world, see how \"man git-add\" describes the behavior\nof ignore/exclude:\n\n       The git add command will not add ignored files by default. If any\n       ignored files were explicitly specified on the command line, git\n       add will fail with a list of ignored files. Ignored files reached\n       by directory recursion or filename globbing performed by Git\n       (quote your globs before the shell) will be silently ignored. The\n       git add command can be used to add ignored files with the -f\n       (force) option.\n\nAnother fun fact: google \"git exclude\" gives this as first result\n\n  http://www.kernel.org/pub/software/scm/git/docs/gitignore.html\n\n;-)\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"194220","messageId":"7v1ul3gt7k.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpq395jj3dt.fsf@bauges.imag.fr","subject":"Re: [PATCH 4/4 v8] config: write to $XDG_CONFIG_HOME/git/config file if appropriate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-25T18:08:31Z","receivedAt":"2012-06-25T18:08:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> We can squash this into PATCH 1:\n>\n> --- a/Documentation/git-config.txt\n> +++ b/Documentation/git-config.txt\n> @@ -209,7 +209,9 @@ $XDG_CONFIG_HOME/git/config::\n>         Second user-specific configuration file. If $XDG_CONFIG_HOME is not set\n>         or empty, $HOME/.config/git/config will be used. Any single-valued\n>         variable set in this file will be overwritten by whatever is in\n> -       ~/.gitconfig.\n> +       ~/.gitconfig. Support for this file was added after Git\n> +       1.7.11, so if you sometimes use older versions of Git, it's a\n> +       good idea not to create this file.\n\nOkay.  Thanks.\n"},{"id":"194954","messageId":"877gu9wh05.fsf@thomas.inf.ethz.ch","threadId":"30663","inReplyTo":"1340355806-6894-2-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 1/4 v8] config: read (but not write) from $XDG_CONFIG_HOME/git/config file","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2012-07-12T07:55:22Z","receivedAt":"2012-07-12T07:55:22Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Hi all,\n\nThis patch causes valgrind warnings in t1300.81 (get --path copes with\nunset $HOME) about passing NULL to access():\n\n==25286== Syscall param access(pathname) points to unaddressable byte(s)\n==25286==    at 0x56E2227: access (in /lib64/libc-2.14.1.so)\n==25286==    by 0x48CA42: git_config_early (config.c:948)\n==25286==    by 0x4F0C20: check_repository_format_gently (setup.c:369)\n==25286==    by 0x4F1A52: setup_git_directory_gently_1 (setup.c:531)\n==25286==    by 0x4F24ED: setup_git_directory_gently (setup.c:725)\n==25286==    by 0x405D8C: run_builtin (git.c:287)\n==25286==    by 0x40548E: main (git.c:467)\n==25286==  Address 0x0 is not stack'd, malloc'd or (recently) free'd\n\nAFAICT it is this bit of code:\n\nMatthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> +void home_config_paths(char **global, char **xdg, char *file)\n> +{\n> +\tchar *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n> +\tchar *home = getenv(\"HOME\");\n> +\tchar *to_free = NULL;\n> +\n> +\tif (!home) {\n> +\t\tif (global)\n> +\t\t\t*global = NULL;\n[...]\n>  int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n>  {\n>  \tint ret = 0, found = 0;\n> -\tconst char *home = NULL;\n> +\tchar *xdg_config = NULL;\n> +\tchar *user_config = NULL;\n> +\n> +\thome_config_paths(&user_config, &xdg_config, \"config\");\n>  \n>  \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n>  \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n> @@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n>  \t\tfound += 1;\n>  \t}\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\t\tfound += 1;\n> -\t\t}\n> +\tif (!access(xdg_config, R_OK)) {\n> +\t\tret += git_config_from_file(fn, xdg_config, data);\n> +\t\tfound += 1;\n> +\t}\n> +\n> +\tif (!access(user_config, R_OK)) {\n> +\t\tret += git_config_from_file(fn, user_config, data);\n> +\t\tfound += 1;\n>  \t}\n\nThat is, while the old code was careful about home==NULL, the new code\nchecks this only in home_config_paths(); after that, it does no further\nNULL check.\n\nThere's a similar instance in your changes to cmd_config(), found by\nrunning 'unset HOME; valgrind git config --global --get foo.bar':\n\n==27841== Syscall param access(pathname) points to unaddressable byte(s)\n==27841==    at 0x56E2227: access (in /lib64/libc-2.14.1.so)\n==27841==    by 0x425280: cmd_config (config.c:390)\n==27841==    by 0x405C34: run_builtin (git.c:306)\n==27841==    by 0x40548E: main (git.c:467)\n==27841==  Address 0x0 is not stack'd, malloc'd or (recently) free'd\n\naround here:\n\n> @@ -379,13 +382,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n[...]\n> +\t\tchar *user_config = NULL;\n> +\t\tchar *xdg_config = NULL;\n> +\n> +\t\thome_config_paths(&user_config, &xdg_config, \"config\");\n> +\n> +\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK) &&\n\nCan you fix this?\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"194960","messageId":"1342094660-3052-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"877gu9wh05.fsf@thomas.inf.ethz.ch","subject":"[PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-07-12T12:04:20Z","receivedAt":"2012-07-12T12:04:20Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"When $HOME is unset, home_config_paths fails and returns NULL pointers\nfor user_config and xdg_config. Valgrind complains with Syscall param\naccess(pathname) points to unaddressable byte(s).\n\nDon't call blindly access() on these variables, but test them for\nNULL-ness before.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n> This patch causes valgrind warnings in t1300.81 (get --path copes with\n> unset $HOME) about passing NULL to access():\n\nIndeed. The following patch should fix it.\n\n builtin/config.c | 3 ++-\n config.c         | 4 ++--\n 2 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e8e1c0a..67945b2 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,7 +387,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n+\t\tif (user_config && access(user_config, R_OK) &&\n+\t\t    xdg_config && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\ndiff --git a/config.c b/config.c\nindex d28a499..6b97503 100644\n--- a/config.c\n+++ b/config.c\n@@ -940,12 +940,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\n \n-\tif (!access(xdg_config, R_OK)) {\n+\tif (xdg_config && !access(xdg_config, R_OK)) {\n \t\tret += git_config_from_file(fn, xdg_config, data);\n \t\tfound += 1;\n \t}\n \n-\tif (!access(user_config, R_OK)) {\n+\tif (user_config && !access(user_config, R_OK)) {\n \t\tret += git_config_from_file(fn, user_config, data);\n \t\tfound += 1;\n \t}\n-- \n1.7.11.1.29.g0e8593d.dirty\n"},{"id":"194963","messageId":"87mx35rw5i.fsf@thomas.inf.ethz.ch","threadId":"30663","inReplyTo":"1342094660-3052-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2012-07-12T12:39:21Z","receivedAt":"2012-07-12T12:39:21Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> When $HOME is unset, home_config_paths fails and returns NULL pointers\n> for user_config and xdg_config. Valgrind complains with Syscall param\n> access(pathname) points to unaddressable byte(s).\n>\n> Don't call blindly access() on these variables, but test them for\n> NULL-ness before.\n>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n> ---\n>> This patch causes valgrind warnings in t1300.81 (get --path copes with\n>> unset $HOME) about passing NULL to access():\n>\n> Indeed. The following patch should fix it.\n\nThanks!\n\nTested-by: Thomas Rast <trast@student.ethz.ch>\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"194971","messageId":"7vtxxcc36v.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"1342094660-3052-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-12T17:14:00Z","receivedAt":"2012-07-12T17:14:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> When $HOME is unset, home_config_paths fails and returns NULL pointers\n> for user_config and xdg_config. Valgrind complains with Syscall param\n> access(pathname) points to unaddressable byte(s).\n>\n> Don't call blindly access() on these variables, but test them for\n> NULL-ness before.\n>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n> ---\n>> This patch causes valgrind warnings in t1300.81 (get --path copes with\n>> unset $HOME) about passing NULL to access():\n>\n> Indeed. The following patch should fix it.\n>\n>  builtin/config.c | 3 ++-\n>  config.c         | 4 ++--\n>  2 files changed, 4 insertions(+), 3 deletions(-)\n>\n> diff --git a/builtin/config.c b/builtin/config.c\n> index e8e1c0a..67945b2 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -387,7 +387,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  \n>  \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n>  \n> -\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n> +\t\tif (user_config && access(user_config, R_OK) &&\n> +\t\t    xdg_config && !access(xdg_config, R_OK))\n>  \t\t\tgiven_config_file = xdg_config;\n\nShouldn't we be using xdg_config, if user_config is NULL and\nxdg_config is defined and accessible?\n\nIn other words, isn't the objective of this \"fix\" is to replace any\ncall to access(PATH, PERM) whose PATH can potentially be NULL with\n\n\t(PATH ? access(PATH, PERM) : -1)\n\nbecause we want to pretend access(PATH, PERM) returned an error,\nsaying \"The variable PATH holds a path to the file that is not\naccessible\", when PATH is NULL?\n\nAnd that is equivalent to\n\n\t(!PATH || access(PATH, PERM))\n\nin the context of boolean.  The original\n\n\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n\nbecomes \n\n\tif ((!user_config || access(user_config, R_OK)) &&\n\t    !(!xdg_config || access(xdg_config, R_OK)))\n\nand the latter half of it is the same as\n\n\t    (xdg_config && !access(xdg_config, R_OK))\n\nbut the former half is not. Shouldn't it be\n\n\tif ((!user_config || access(user_config, R_OK)) &&\n\t    (xdg_config && !access(xdg_config, R_OK)))\n\nConfused.\n\n> diff --git a/config.c b/config.c\n> index d28a499..6b97503 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -940,12 +940,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n>  \t\tfound += 1;\n>  \t}\n>  \n> -\tif (!access(xdg_config, R_OK)) {\n> +\tif (xdg_config && !access(xdg_config, R_OK)) {\n>  \t\tret += git_config_from_file(fn, xdg_config, data);\n>  \t\tfound += 1;\n>  \t}\n>  \n> -\tif (!access(user_config, R_OK)) {\n> +\tif (user_config && !access(user_config, R_OK)) {\n>  \t\tret += git_config_from_file(fn, user_config, data);\n>  \t\tfound += 1;\n>  \t}\n"},{"id":"194979","messageId":"vpq1ukgai4e.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vtxxcc36v.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-12T19:34:25Z","receivedAt":"2012-07-12T19:34:25Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n>\n>> When $HOME is unset, home_config_paths fails and returns NULL pointers\n>> for user_config and xdg_config. Valgrind complains with Syscall param\n>> access(pathname) points to unaddressable byte(s).\n>>\n>> Don't call blindly access() on these variables, but test them for\n>> NULL-ness before.\n>>\n>> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n>> ---\n>>> This patch causes valgrind warnings in t1300.81 (get --path copes with\n>>> unset $HOME) about passing NULL to access():\n>>\n>> Indeed. The following patch should fix it.\n>>\n>>  builtin/config.c | 3 ++-\n>>  config.c         | 4 ++--\n>>  2 files changed, 4 insertions(+), 3 deletions(-)\n>>\n>> diff --git a/builtin/config.c b/builtin/config.c\n>> index e8e1c0a..67945b2 100644\n>> --- a/builtin/config.c\n>> +++ b/builtin/config.c\n>> @@ -387,7 +387,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>>  \n>>  \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n>>  \n>> -\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n>> +\t\tif (user_config && access(user_config, R_OK) &&\n>> +\t\t    xdg_config && !access(xdg_config, R_OK))\n>>  \t\t\tgiven_config_file = xdg_config;\n>\n> Shouldn't we be using xdg_config, if user_config is NULL and\n> xdg_config is defined and accessible?\n\nI don't think so. If user_config is NULL, it means something went wrong,\nbecause $HOME is unset. In this case, I'd rather die than using some\nother configuration file silently (which would be possible if\n$XDG_CONFIG_HOME is set), and this is what the code does:\n\n\t\tif (user_config && access(user_config, R_OK) &&\n\t\t    xdg_config && !access(xdg_config, R_OK))\n\t\t\tgiven_config_file = xdg_config;\n\t\telse if (user_config)\n\t\t\tgiven_config_file = user_config;\n\t\telse\n\t\t\tdie(\"$HOME not set\");\n\nPerhaps it could actually be made even clearer with\n\n\t\tif (!user_config)\n\t\t\tdie(\"$HOME not set\");\n\t\telse if (access(user_config, R_OK) &&\n\t\t\t xdg_config && !access(xdg_config, R_OK))\n\t\t\tgiven_config_file = xdg_config;\n\t\telse\n\t\t\tgiven_config_file = user_config;\n\nThat said, I don't care very strongly about it.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"194982","messageId":"7vwr28agcq.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpq1ukgai4e.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-12T20:12:37Z","receivedAt":"2012-07-12T20:12:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n>>> +\t\tif (user_config && access(user_config, R_OK) &&\n>>> +\t\t    xdg_config && !access(xdg_config, R_OK))\n>>>  \t\t\tgiven_config_file = xdg_config;\n>>\n>> Shouldn't we be using xdg_config, if user_config is NULL and\n>> xdg_config is defined and accessible?\n>\n> I don't think so. If user_config is NULL, it means something went wrong,\n> because $HOME is unset. In this case, I'd rather die than using some\n> other configuration file silently (which would be possible if\n> $XDG_CONFIG_HOME is set), and this is what the code does:\n>\n> \t\tif (user_config && access(user_config, R_OK) &&\n> \t\t    xdg_config && !access(xdg_config, R_OK))\n> \t\t\tgiven_config_file = xdg_config;\n> \t\telse if (user_config)\n> \t\t\tgiven_config_file = user_config;\n> \t\telse\n> \t\t\tdie(\"$HOME not set\");\n>\n> Perhaps it could actually be made even clearer with\n>\n> \t\tif (!user_config)\n> \t\t\tdie(\"$HOME not set\");\n> \t\telse if (access(user_config, R_OK) &&\n> \t\t\t xdg_config && !access(xdg_config, R_OK))\n> \t\t\tgiven_config_file = xdg_config;\n> \t\telse\n> \t\t\tgiven_config_file = user_config;\n\nAt least the code needs to break lines at different point and a comment.\n\n\tif (user_config &&\n\t\taccess(user_config, R_OK) &&\n                (xdg_config && !access(xdg_config, R_OK)))\n                /*\n                 * Even if we have usable XDG stuff, we want to\n\t\t * error out on missing HOME!!!\n                 */\n\t\tuse xdg config;\n\telse if (user_config)\n\t\tuse user config;\n\telse\n\t\tunusable situation;\n\nBut is it really true that we want to error out on missing HOME if\nwe have usable XDG stuff?\n"},{"id":"194999","messageId":"vpq1ukg82st.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vwr28agcq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-13T08:48:18Z","receivedAt":"2012-07-13T08:48:18Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> But is it really true that we want to error out on missing HOME if\n> we have usable XDG stuff?\n\nAnyone else have an opinion on this?\n\nIn short, the question is whether\n\n  export XDG_CONFIG_HOME=some-existing-dir\n  unset HOME\n  git config foo.baz boz\n\nshould die(\"$HOME is unset\") or use the XDG config file.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195001","messageId":"1342169994-11969-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"30663","inReplyTo":"vpq1ukg82st.fsf@bauges.imag.fr","subject":"[PATCH v2] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2012-07-13T08:59:54Z","receivedAt":"2012-07-13T08:59:54Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"When $HOME is unset, home_config_paths fails and returns NULL pointers\nfor user_config and xdg_config. Valgrind complains with Syscall param\naccess(pathname) points to unaddressable byte(s).\n\nDon't call blindly access() on these variables, but test them for\nNULL-ness before.\n\nThe when the XDG configuration file can be found but not $HOME/.gitconfig\nrequires a bit of attention. We chose to error out in \"git config --set\"\nif $HOME is unset anyway.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n\nBefore I forget about it, here's the patch assuming people do want to\nerror out when $HOME is unset. It should be functionally equivalent to\nthe previous one, but the code should be clearer.\n\n builtin/config.c | 15 +++++++++++----\n config.c         |  4 ++--\n 2 files changed, 13 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex e8e1c0a..f064d6e 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,12 +387,19 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n+\t\tif (!user_config)\n+\t\t\t/*\n+\t\t\t * Don't even try to access the xdg_config, as\n+\t\t\t * unset $HOME means something is really\n+\t\t\t * broken and should be fixed. Silently\n+\t\t\t * writing to xdg_config may be confusing.\n+\t\t\t */\n+\t\t\tdie(\"$HOME not set\");\n+\t\telse if (access(user_config, R_OK) &&\n+\t\t\t xdg_config && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n-\t\telse if (user_config)\n-\t\t\tgiven_config_file = user_config;\n \t\telse\n-\t\t\tdie(\"$HOME not set\");\n+\t\t\tgiven_config_file = user_config;\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\ndiff --git a/config.c b/config.c\nindex d28a499..6b97503 100644\n--- a/config.c\n+++ b/config.c\n@@ -940,12 +940,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \t\tfound += 1;\n \t}\n \n-\tif (!access(xdg_config, R_OK)) {\n+\tif (xdg_config && !access(xdg_config, R_OK)) {\n \t\tret += git_config_from_file(fn, xdg_config, data);\n \t\tfound += 1;\n \t}\n \n-\tif (!access(user_config, R_OK)) {\n+\tif (user_config && !access(user_config, R_OK)) {\n \t\tret += git_config_from_file(fn, user_config, data);\n \t\tfound += 1;\n \t}\n-- \n1.7.11.1.30.g7e1baf9.dirty\n"},{"id":"195003","messageId":"20120713130021.GA2553@sigill.intra.peff.net","threadId":"30663","inReplyTo":"vpq1ukg82st.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-07-13T13:00:22Z","receivedAt":"2012-07-13T13:00:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 13, 2012 at 10:48:18AM +0200, Matthieu Moy wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > But is it really true that we want to error out on missing HOME if\n> > we have usable XDG stuff?\n> \n> Anyone else have an opinion on this?\n> \n> In short, the question is whether\n> \n>   export XDG_CONFIG_HOME=some-existing-dir\n>   unset HOME\n>   git config foo.baz boz\n> \n> should die(\"$HOME is unset\") or use the XDG config file.\n\nWhat did previous versions of git do? From my reading of 21cf32279, the\nprevious behavior was that if $HOME was not set, git would silently\navoid reading from $HOME/.gitconfig entirely. Wouldn't dying be a huge\nregression?\n\n-Peff\n"},{"id":"195005","messageId":"vpqliin6bva.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"20120713130021.GA2553@sigill.intra.peff.net","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-13T13:15:21Z","receivedAt":"2012-07-13T13:15:21Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Jul 13, 2012 at 10:48:18AM +0200, Matthieu Moy wrote:\n>\n>> Junio C Hamano <gitster@pobox.com> writes:\n>> \n>> > But is it really true that we want to error out on missing HOME if\n>> > we have usable XDG stuff?\n>> \n>> Anyone else have an opinion on this?\n>> \n>> In short, the question is whether\n>> \n>>   export XDG_CONFIG_HOME=some-existing-dir\n>>   unset HOME\n>>   git config foo.baz boz\n>> \n>> should die(\"$HOME is unset\") or use the XDG config file.\n>\n> What did previous versions of git do? From my reading of 21cf32279, the\n> previous behavior was that if $HOME was not set, git would silently\n> avoid reading from $HOME/.gitconfig entirely.\n\nYes, and this is still the case for _reading_. But the current case is\nabout deciding which file to use when _writing_. Git was already dying\nwhen writing with an unset $HOME. There is no behavior change in this\ncase.\n\nWith Junio's suggestion, we would have a behavior change in that we\nwould write to the XDG file if we can find it (using XDG_CONFIG_HOME,\nobviously, since $HOME is unset in this case).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195007","messageId":"87hatboiwr.fsf@thomas.inf.ethz.ch","threadId":"30663","inReplyTo":"vpqliin6bva.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2012-07-13T14:05:56Z","receivedAt":"2012-07-13T14:05:56Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Jeff King <peff@peff.net> writes:\n>\n>> On Fri, Jul 13, 2012 at 10:48:18AM +0200, Matthieu Moy wrote:\n>>\n>>> Junio C Hamano <gitster@pobox.com> writes:\n>>> \n>>> > But is it really true that we want to error out on missing HOME if\n>>> > we have usable XDG stuff?\n>>> \n>>> Anyone else have an opinion on this?\n>>> \n>>> In short, the question is whether\n>>> \n>>>   export XDG_CONFIG_HOME=some-existing-dir\n>>>   unset HOME\n>>>   git config foo.baz boz\n>>> \n>>> should die(\"$HOME is unset\") or use the XDG config file.\n>>\n>> What did previous versions of git do? From my reading of 21cf32279, the\n>> previous behavior was that if $HOME was not set, git would silently\n>> avoid reading from $HOME/.gitconfig entirely.\n>\n> Yes, and this is still the case for _reading_. But the current case is\n> about deciding which file to use when _writing_. Git was already dying\n> when writing with an unset $HOME.\n\nUmm, are you sure?  I may be somewhat confused about this, but the tests\nI used to trigger the access(NULL) were IIRC\n\n  unset HOME\n  git config --get foo.bar\n  git config --global --get foo.bar\n\nnone of which is writing....\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"195008","messageId":"vpq1ukf68ow.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"87hatboiwr.fsf@thomas.inf.ethz.ch","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-13T14:23:59Z","receivedAt":"2012-07-13T14:23:59Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Umm, are you sure?  I may be somewhat confused about this, but the tests\n> I used to trigger the access(NULL) were IIRC\n>\n>   unset HOME\n>   git config --get foo.bar\n>   git config --global --get foo.bar\n>\n> none of which is writing....\n\nI was inaccurate, but that doesn't change the conclusion: the question\nis not reading Vs writting, but use of --global Vs no use of it (and\nmost of the time, --global is used for writing, hence my confusion).\n\nBoth the patch that introduce the NULL bug and the one that fix it touch\ntwo files: $git/config.c, and $git/builtin/config.c.\n\nMy changes to $git/config.c, are straightforward because they touch\npositive tests for file existance. The controversial one is\n$git/builtin/config.c, which is inside a \"if (use_global_config)\".\n\nThis piece of code was already dying on unset $HOME, and in my proposal\nit still is.\n\nThe old code was:\n\n        if (use_global_config) {\n                char *home = getenv(\"HOME\");\n                if (home) {\n                        char *user_config = xstrdup(mkpath(\"%s/.gitconfig\", home));\n                        given_config_file = user_config;\n                } else {\n                        die(\"$HOME not set\");\n                }\n        }\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195014","messageId":"7vy5mn8v2k.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpq1ukf68ow.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-13T16:49:55Z","receivedAt":"2012-07-13T16:49:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> The old code was:\n>\n>         if (use_global_config) {\n>                 char *home = getenv(\"HOME\");\n>                 if (home) {\n>                         char *user_config = xstrdup(mkpath(\"%s/.gitconfig\", home));\n>                         given_config_file = user_config;\n>                 } else {\n>                         die(\"$HOME not set\");\n>                 }\n>         }\n\nI think the above is a good illustration of the crux of the issue,\nas the natural way to introduce XDG that is used when the one at\n$HOME/.gitconfig is not there seems at least to me to do this:\n\n\tif (use_global_config) {\n                if (is $HOME/.gitconfig usable?) {\n\t\t\tuse it;\n\t\t} else if (is $XDG/git/config usable?) {\n                \tuse $XDG location;\n\t\t} else {\n\t\t\tdie(\"Neither $XDG or $HOME is usable\");\n\t\t}\n\t}\n\nIn other words, we are not trying to say \"You do not have $HOME --\nthere is something wrong, so we will give up\" in this codepath.  We\nwere saying \"Can we use _the_ place we place personal config?  It is\nan error if we cannot find it, so we die.\" and the above says \"Can\nwe use this place?  Can we use that place?  If no place is found to\nbe usable, we die.\"\n\nAnd the intent of the \"NULL-buggy\" version seems to want the same\n\"If we cannot use home/.gitconfig but xdg_config one is usable, use\nxdg one\" semantics to me.\n\n\t\tif (access(user_config, R_OK) && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n \t\telse if (user_config)\n \t\t\tgiven_config_file = user_config;\n"},{"id":"195099","messageId":"vpqmx3011la.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vy5mn8v2k.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-16T09:45:21Z","receivedAt":"2012-07-16T09:45:21Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \tif (use_global_config) {\n>                 if (is $HOME/.gitconfig usable?) {\n> \t\t\tuse it;\n\nYes, but when $HOME is unset, the question doesn't really make sense.\nMaybe the file exists, but we can't know since the user broke his\nconfiguration by unsetting $HOME. The intent was to avoid writing to the\nXDG file unless it was very clear that the user wanted it, so in doubt,\ndying seems the best option.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195126","messageId":"7vipdn4qbk.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpqmx3011la.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-16T16:35:11Z","receivedAt":"2012-07-16T16:35:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> \tif (use_global_config) {\n>>                 if (is $HOME/.gitconfig usable?) {\n>> \t\t\tuse it;\n>\n> Yes, but when $HOME is unset, the question doesn't really make sense.\n> Maybe the file exists, but we can't know since the user broke his\n> configuration by unsetting $HOME. The intent was to avoid writing to the\n> XDG file unless it was very clear that the user wanted it, so in doubt,\n> dying seems the best option.\n\nI would think that it is plausible that the user wanted to write\ninto XDG one and used \"unset HOME\" as a way to signal that wish. \n\nAre there ways to force writing into XDG ones without having to\nremove the $HOME/ ones (perhaps the user wants to keep them for use\nwith older versions of Git on a different machine that shares the\nsame $HOME directory)?  Temporarily unsetting HOME may be how a user\nmight achieve it.\n\nIf you want to disallow such a use case, that is fine, but at least\nthe logic needs to be described in comment.  Perhaps based on one of\nyour rewrites earlier in the thread, like this?\n\n builtin/config.c | 21 +++++++++++++++++----\n 1 file changed, 17 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 67945b2..a788409 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -387,13 +387,26 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \n \t\thome_config_paths(&user_config, &xdg_config, \"config\");\n \n-\t\tif (user_config && access(user_config, R_OK) &&\n+\t\tif (!user_config)\n+\t\t\t/*\n+\t\t\t * We do not know HOME/.gitconfig exists or\n+\t\t\t * not, hence we do not know if we should\n+\t\t\t * write to XDG location, so we error out even\n+\t\t\t * if XDG_CONFIG_HOME is set and points at a\n+\t\t\t * sane location.\n+\t\t\t *\n+\t\t\t * In other words, we forbid the user from\n+\t\t\t * telling us to write to XDG location,\n+\t\t\t * pretending that $HOME/.gitconfig does not\n+\t\t\t * exist, by temporarily unsetting HOME.\n+\t\t\t */\n+\t\t\tdie(\"$HOME not set\");\n+\n+\t\tif (access(user_config, R_OK) &&\n \t\t    xdg_config && !access(xdg_config, R_OK))\n \t\t\tgiven_config_file = xdg_config;\n-\t\telse if (user_config)\n-\t\t\tgiven_config_file = user_config;\n \t\telse\n-\t\t\tdie(\"$HOME not set\");\n+\t\t\tgiven_config_file = user_config;\n \t}\n \telse if (use_system_config)\n \t\tgiven_config_file = git_etc_gitconfig();\n"},{"id":"195127","messageId":"vpq7gu3wtgo.fsf@bauges.imag.fr","threadId":"30663","inReplyTo":"7vipdn4qbk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-07-16T16:39:51Z","receivedAt":"2012-07-16T16:39:51Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I would think that it is plausible that the user wanted to write\n> into XDG one and used \"unset HOME\" as a way to signal that wish. \n\nI didn't think of this case, but it makes sense.\n\nAnyway, I don't really care either way, so I leave it up to you (either\nyour patch below, or the fixup in pu).\n\n> -\t\tif (user_config && access(user_config, R_OK) &&\n> +\t\tif (!user_config)\n> +\t\t\t/*\n> +\t\t\t * We do not know HOME/.gitconfig exists or\n> +\t\t\t * not, hence we do not know if we should\n> +\t\t\t * write to XDG location, so we error out even\n> +\t\t\t * if XDG_CONFIG_HOME is set and points at a\n> +\t\t\t * sane location.\n> +\t\t\t *\n> +\t\t\t * In other words, we forbid the user from\n> +\t\t\t * telling us to write to XDG location,\n> +\t\t\t * pretending that $HOME/.gitconfig does not\n> +\t\t\t * exist, by temporarily unsetting HOME.\n> +\t\t\t */\n> +\t\t\tdie(\"$HOME not set\");\n> +\n> +\t\tif (access(user_config, R_OK) &&\n>  \t\t    xdg_config && !access(xdg_config, R_OK))\n>  \t\t\tgiven_config_file = xdg_config;\n> -\t\telse if (user_config)\n> -\t\t\tgiven_config_file = user_config;\n>  \t\telse\n> -\t\t\tdie(\"$HOME not set\");\n> +\t\t\tgiven_config_file = user_config;\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"195130","messageId":"7vbojf4pbt.fsf@alter.siamese.dyndns.org","threadId":"30663","inReplyTo":"vpq7gu3wtgo.fsf@bauges.imag.fr","subject":"Re: [PATCH] config: fix several access(NULL) calls","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-16T16:56:38Z","receivedAt":"2012-07-16T16:56:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> I would think that it is plausible that the user wanted to write\n>> into XDG one and used \"unset HOME\" as a way to signal that wish. \n>\n> I didn't think of this case, but it makes sense.\n>\n> Anyway, I don't really care either way, so I leave it up to you (either\n> your patch below, or the fixup in pu).\n\nI do not care deeply either, as there always is \"config -f $file\" if\nsomebody wants to write into a specific location.\n\nBut if you do something that is different from a straight-and-dumb\nconversion from access(A, B) to (A ? access(A, B) : -1), the\nconversion is bound to be questioned by somebody in 2 years \"isn't\nthis a misconversion?\".  If you did that on purpose, a comment to\nexplain why it is not a misconversion would help them understand the\nreasoning behind it.\n"}]}