{"thread":{"id":"21635","subject":"[PATCH] Expand ~ and ~user in core.excludesfile, commit.template","startedAt":"2009-11-16T10:07:40Z","lastAt":"2009-11-19T18:12:54Z","messageCount":24,"participants":["Matthieu Moy","Junio C Hamano","Jakub Narebski","Jeff King","Mike Hommey","Andreas Schwab"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"127668","messageId":"1258366060-27966-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"21635","inReplyTo":null,"subject":"[PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-11-16T10:07:40Z","receivedAt":"2009-11-16T10:07:40Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"These config variables are parsed to substitute ~ and ~user with getpw\nentries.\n\nuser_path() refactored into new function expand_user_path(), to allow\ndynamically allocating the return buffer.\n\nOriginal patch by Karl Chen, modified by Matthieu Moy.\n\nSigned-off-by: Karl Chen <quarl@quarl.org>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\nThe original thread was here:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/93250/focus=94276\n\nMy version is made a bit simpler by using strbuf for string\nmanipulation in expand_user_path.\n\nI'm not sure I fully adressed Junio's point here:\nhttp://thread.gmane.org/gmane.comp.version-control.git/93250/focus=94276\n\n> I do not see any strong reason why the single caller of user_path() has to\n> keep using the static allocation.  Would it help to reduce the complexity\n> of your expand_user_path() implementation, if we fixed the caller along\n> the lines of this patch (untested, but just to illustrate the point)?\n> \n> [...]\n> --- i/path.c\n> +++ w/path.c\n> @@ -221,19 +221,22 @@ char *enter_repo(char *path, int strict)\n>  \t\tif (PATH_MAX <= len)\n>  \t\t\treturn NULL;\n>  \t\tif (path[0] == '~') {\n> -\t\t\tif (!user_path(used_path, path, PATH_MAX))\n> +\t\t\tchar *newpath = expand_user_path(path);\n> [...]\n\nI'm just copying back into the static buffer to let enter_repo() do\nthe same string manipulation as it used to do (concatenate with .git\nsuffixes). I think the whole enter_repo could use strbuf instead of\nstatic buffers, but that's a different point (probably made easier by\nmy patch).\n\n Documentation/config.txt |    4 ++-\n builtin-commit.c         |    2 +-\n cache.h                  |    2 +\n config.c                 |   11 +++++-\n path.c                   |   84 +++++++++++++++++++++++++++------------------\n 5 files changed, 66 insertions(+), 37 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d1e2120..c37b51d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -380,7 +380,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n core.excludesfile::\n \tIn addition to '.gitignore' (per-directory) and\n \t'.git/info/exclude', git looks into this file for patterns\n-\tof files which are not meant to be tracked.  See\n+\tof files which are not meant to be tracked.  \"~\" and \"~user\"\n+\tare expanded to the user's home directory.  See\n \tlinkgit:gitignore[5].\n \n core.editor::\n@@ -670,6 +671,7 @@ color.ui::\n \n commit.template::\n \tSpecify a file to use as the template for new commit messages.\n+\t\"~\" and \"~user\" are expanded to the user's home directory.\n \n diff.autorefreshindex::\n \tWhen using 'git-diff' to compare with work tree\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex d525b89..09d2840 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -999,7 +999,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \tstruct wt_status *s = cb;\n \n \tif (!strcmp(k, \"commit.template\"))\n-\t\treturn git_config_string(&template_file, k, v);\n+\t\treturn git_config_pathname(&template_file, k, v);\n \n \treturn git_status_config(k, v, s);\n }\ndiff --git a/cache.h b/cache.h\nindex 71a731d..42f7cd8 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -645,6 +645,7 @@ int set_shared_perm(const char *path, int mode);\n #define adjust_shared_perm(path) set_shared_perm((path), 0)\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n+extern char *expand_user_path(const char *path);\n char *enter_repo(char *path, int strict);\n static inline int is_absolute_path(const char *path)\n {\n@@ -903,6 +904,7 @@ extern unsigned long git_config_ulong(const char *, const char *);\n extern int git_config_bool_or_int(const char *, const char *, int *);\n extern int git_config_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n+extern int git_config_pathname(const char **, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_set_multivar(const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex c644061..0fcc4ce 100644\n--- a/config.c\n+++ b/config.c\n@@ -351,6 +351,15 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n+int git_config_pathname(const char **dest, const char *var, const char *value) {\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\t*dest = expand_user_path(value);\n+\tif (!*dest)\n+\t\tdie(\"Failed to expand user dir in: '%s'\", value);\n+\treturn 0;\n+}\n+\n static int git_default_core_config(const char *var, const char *value)\n {\n \t/* This needs a better name */\n@@ -474,7 +483,7 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn git_config_string(&editor_program, var, value);\n \n \tif (!strcmp(var, \"core.excludesfile\"))\n-\t\treturn git_config_string(&excludes_file, var, value);\n+\t\treturn git_config_pathname(&excludes_file, var, value);\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n \t\tif (!value)\ndiff --git a/path.c b/path.c\nindex 047fdb0..009c8e0 100644\n--- a/path.c\n+++ b/path.c\n@@ -11,6 +11,7 @@\n  * which is what it's designed for.\n  */\n #include \"cache.h\"\n+#include \"strbuf.h\"\n \n static char bad_path[] = \"/bad-path/\";\n \n@@ -207,43 +208,49 @@ int validate_headref(const char *path)\n \treturn -1;\n }\n \n-static char *user_path(char *buf, char *path, int sz)\n+static inline struct passwd *getpw_str(const char *username, size_t len)\n {\n+\tif (len == 0)\n+\t\treturn getpwuid(getuid());\n+\n \tstruct passwd *pw;\n-\tchar *slash;\n-\tint len, baselen;\n+\tchar *username_z = xmalloc(len + 1);\n+\tmemcpy(username_z, username, len);\n+\tusername_z[len] = '\\0';\n+\tpw = getpwnam(username_z);\n+\tfree(username_z);\n+\treturn pw;\n+}\n \n-\tif (!path || path[0] != '~')\n-\t\treturn NULL;\n-\tpath++;\n-\tslash = strchr(path, '/');\n-\tif (path[0] == '/' || !path[0]) {\n-\t\tpw = getpwuid(getuid());\n-\t}\n-\telse {\n-\t\tif (slash) {\n-\t\t\t*slash = 0;\n-\t\t\tpw = getpwnam(path);\n-\t\t\t*slash = '/';\n-\t\t}\n-\t\telse\n-\t\t\tpw = getpwnam(path);\n-\t}\n-\tif (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))\n-\t\treturn NULL;\n-\tbaselen = strlen(pw->pw_dir);\n-\tmemcpy(buf, pw->pw_dir, baselen);\n-\twhile ((1 < baselen) && (buf[baselen-1] == '/')) {\n-\t\tbuf[baselen-1] = 0;\n-\t\tbaselen--;\n-\t}\n-\tif (slash && slash[1]) {\n-\t\tlen = strlen(slash);\n-\t\tif (sz <= baselen + len)\n-\t\t\treturn NULL;\n-\t\tmemcpy(buf + baselen, slash, len + 1);\n+/*\n+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL, then\n+ * it is a newly allocated string. Returns NULL on getpw failure or if\n+ * path is NULL.\n+ */\n+char *expand_user_path(const char *path)\n+{\n+\tstruct strbuf user_path = STRBUF_INIT;\n+\tchar * first_slash = strchrnul(path, '/');\n+\tchar * to_copy;\n+\tif (path == NULL)\n+\t\tgoto return_null;\n+\n+\tif (path[0] == '~') {\n+\t\tconst char *username = path + 1;\n+\t\tsize_t username_len = first_slash - username;\n+\t\tstruct passwd *pw = getpw_str(username, username_len);\n+\t\tif (!pw)\n+\t\t\tgoto return_null;\n+\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n+\t\tto_copy = first_slash;\n+\t} else if (path[0] != '/') {\n+\t\tto_copy = path;\n \t}\n-\treturn buf;\n+\tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n+\treturn strbuf_detach(&user_path, NULL);\n+return_null:\n+\tstrbuf_release(&user_path);\n+\treturn NULL;\n }\n \n /*\n@@ -291,8 +298,17 @@ char *enter_repo(char *path, int strict)\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n \t\tif (path[0] == '~') {\n-\t\t\tif (!user_path(used_path, path, PATH_MAX))\n+\t\t\tchar *newpath = expand_user_path(path);\n+\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n+\t\t\t\tif (path != newpath)\n+\t\t\t\t\tfree(newpath);\n \t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\t/* Copy back into the static buffer. A pity\n+\t\t\t   since newpath was not bounded, but other\n+\t\t\t   branches of the if are limited by PATH_MAX\n+\t\t\t   anyway. */\n+\t\t\tstrcpy(used_path, newpath); free(newpath);\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\n \t\t}\n-- \n1.6.5.2.152.gbbe9e\n"},{"id":"127705","messageId":"7v3a4enkzt.fsf@alter.siamese.dyndns.org","threadId":"21635","inReplyTo":"1258366060-27966-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-16T22:49:26Z","receivedAt":"2009-11-16T22:49:26Z","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> My version is made a bit simpler by using strbuf for string\n> manipulation in expand_user_path.\n\nNice to see people keeping track of issues that we tried to address but\ndidn't complete.\n\n> I'm not sure I fully adressed Junio's point here:\n\nWe'll see ;-)\n\n> I'm just copying back into the static buffer to let enter_repo() do\n> the same string manipulation as it used to do (concatenate with .git\n> suffixes). I think the whole enter_repo could use strbuf instead of\n> static buffers, but that's a different point (probably made easier by\n> my patch).\n\n> diff --git a/config.c b/config.c\n> index c644061..0fcc4ce 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -351,6 +351,15 @@ int git_config_string(const char **dest, const char *var, const char *value)\n>  \treturn 0;\n>  }\n>  \n> +int git_config_pathname(const char **dest, const char *var, const char *value) {\n> +\tif (!value)\n> +\t\treturn config_error_nonbool(var);\n> +\t*dest = expand_user_path(value);\n> +\tif (!*dest)\n> +\t\tdie(\"Failed to expand user dir in: '%s'\", value);\n> +\treturn 0;\n> +}\n> +\n\n> @@ -207,43 +208,49 @@ int validate_headref(const char *path)\n>  \treturn -1;\n>  }\n>  \n> -static char *user_path(char *buf, char *path, int sz)\n> +static inline struct passwd *getpw_str(const char *username, size_t len)\n>  {\n> +\tif (len == 0)\n> +\t\treturn getpwuid(getuid());\n> +\n>  \tstruct passwd *pw;\n\nDecl-after-statement.\n\nDo you need this special case of passing a zero-length string (not NULL\npointer as a string) to getpw_str() to grab the current user?  Which\ncodepath is this needed?\n\n> +\tchar *username_z = xmalloc(len + 1);\n> +\tmemcpy(username_z, username, len);\n> +\tusername_z[len] = '\\0';\n> +\tpw = getpwnam(username_z);\n> +\tfree(username_z);\n> +\treturn pw;\n> +}\n\n> +/*\n> + * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL, then\n> + * it is a newly allocated string. Returns NULL on getpw failure or if\n> + * path is NULL.\n> + */\n> +char *expand_user_path(const char *path)\n> +{\n> +\tstruct strbuf user_path = STRBUF_INIT;\n> +\tchar * first_slash = strchrnul(path, '/');\n> +\tchar * to_copy;\n\nStyle: \"char *first_slash\"\n\nShould't \"to_copy\" be initialized to \"path\" here?  What do you copy when\npath[0] is '/'?\n\n> +\tif (path == NULL)\n> +\t\tgoto return_null;\n> +\n> +\tif (path[0] == '~') {\n> +\t\tconst char *username = path + 1;\n> +\t\tsize_t username_len = first_slash - username;\n> +\t\tstruct passwd *pw = getpw_str(username, username_len);\n> +\t\tif (!pw)\n> +\t\t\tgoto return_null;\n> +\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n> +\t\tto_copy = first_slash;\n> +\t} else if (path[0] != '/') {\n> +\t\tto_copy = path;\n>  \t}\n> -\treturn buf;\n> +\tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n> +\treturn strbuf_detach(&user_path, NULL);\n> +return_null:\n> +\tstrbuf_release(&user_path);\n> +\treturn NULL;\n>  }\n\n\n\n>  /*\n> @@ -291,8 +298,17 @@ char *enter_repo(char *path, int strict)\n>  \t\tif (PATH_MAX <= len)\n>  \t\t\treturn NULL;\n>  \t\tif (path[0] == '~') {\n> -\t\t\tif (!user_path(used_path, path, PATH_MAX))\n> +\t\t\tchar *newpath = expand_user_path(path);\n> +\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n> +\t\t\t\tif (path != newpath)\n> +\t\t\t\t\tfree(newpath);\n\nYour expand_user_path() never returns the original, no?\n\n>  \t\t\t\treturn NULL;\n> +\t\t\t}\n> +\t\t\t/* Copy back into the static buffer. A pity\n> +\t\t\t   since newpath was not bounded, but other\n> +\t\t\t   branches of the if are limited by PATH_MAX\n> +\t\t\t   anyway. */\n> +\t\t\tstrcpy(used_path, newpath); free(newpath);\n>  \t\t\tstrcpy(validated_path, path);\n>  \t\t\tpath = used_path;\n>  \t\t}\n\nHeh, in a sense you _did_ address my point by punting, but that is Ok.  As\nyou said earlier that would be a good topic of a separate patch.\n\n\t/*\n         * By the way, we write our\n         * multi-line comments like\n         * this.\n         */\n"},{"id":"127711","messageId":"m3k4xqc9qj.fsf@localhost.localdomain","threadId":"21635","inReplyTo":"1258366060-27966-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-11-16T23:47:29Z","receivedAt":"2009-11-16T23:47:29Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> These config variables are parsed to substitute ~ and ~user with getpw\n> entries.\n> \n> user_path() refactored into new function expand_user_path(), to allow\n> dynamically allocating the return buffer.\n> \n> Original patch by Karl Chen, modified by Matthieu Moy.\n> \n> Signed-off-by: Karl Chen <quarl@quarl.org>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n> ---\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index d1e2120..c37b51d 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -380,7 +380,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n>  core.excludesfile::\n>  \tIn addition to '.gitignore' (per-directory) and\n>  \t'.git/info/exclude', git looks into this file for patterns\n> -\tof files which are not meant to be tracked.  See\n> +\tof files which are not meant to be tracked.  \"~\" and \"~user\"\n> +\tare expanded to the user's home directory.  See\n>  \tlinkgit:gitignore[5].\n>  \n>  core.editor::\n\nIt would be nice to have an option to git-config which would do such\nexpansion, as a separate type similar to --int and --bool, e.g.:\n\n  git config --path section.key\n\nso that not only core.excludesfile could use this new feature, but for\nexample also core.worktree, commit.template, gitcvs.logfile,\nmailmap.file, and perhaps also *.receivepack and *.uploadpack\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"127735","messageId":"7viqd9isbm.fsf@alter.siamese.dyndns.org","threadId":"21635","inReplyTo":"m3k4xqc9qj.fsf@localhost.localdomain","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-17T06:22:21Z","receivedAt":"2009-11-17T06:22:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n>> diff --git a/Documentation/config.txt b/Documentation/config.txt\n>> index d1e2120..c37b51d 100644\n>> --- a/Documentation/config.txt\n>> +++ b/Documentation/config.txt\n>> @@ -380,7 +380,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n>>  core.excludesfile::\n>>  \tIn addition to '.gitignore' (per-directory) and\n>>  \t'.git/info/exclude', git looks into this file for patterns\n>> -\tof files which are not meant to be tracked.  See\n>> +\tof files which are not meant to be tracked.  \"~\" and \"~user\"\n>> +\tare expanded to the user's home directory.  See\n>>  \tlinkgit:gitignore[5].\n>>  \n>>  core.editor::\n>\n> It would be nice to have an option to git-config which would do such\n> expansion, as a separate type similar to --int and --bool, e.g.:\n>\n>   git config --path section.key\n>\n> so that not only core.excludesfile could use this new feature, but for\n> example also core.worktree, commit.template, gitcvs.logfile,\n> mailmap.file, and perhaps also *.receivepack and *.uploadpack\n\nWhat should \"git config -l\" do for these (and core.excludesfile)?\n"},{"id":"127741","messageId":"7vy6m5hci0.fsf@alter.siamese.dyndns.org","threadId":"21635","inReplyTo":"7v3a4enkzt.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-17T06:49:27Z","receivedAt":"2009-11-17T06:49:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I'd like to squash this to your patch, based on the earlier review\ncomments.\n\nI didn't mention about the first hunk; it is just a style.  An opening\nbrace that begins a function body comes at column 1.\n\nAlso first_slash and to_copy are made const pointers, as they do not have\nto touch the region of memory they point to (otherwise you cannot assign\npath to to_copy without getting warned).\n\n config.c |    3 ++-\n path.c   |   32 ++++++++++++++------------------\n 2 files changed, 16 insertions(+), 19 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 0fcc4ce..b3d1ff4 100644\n--- a/config.c\n+++ b/config.c\n@@ -351,7 +351,8 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n-int git_config_pathname(const char **dest, const char *var, const char *value) {\n+int git_config_pathname(const char **dest, const char *var, const char *value)\n+{\n \tif (!value)\n \t\treturn config_error_nonbool(var);\n \t*dest = expand_user_path(value);\ndiff --git a/path.c b/path.c\nindex 009c8e0..2470f78 100644\n--- a/path.c\n+++ b/path.c\n@@ -208,11 +208,8 @@ int validate_headref(const char *path)\n \treturn -1;\n }\n \n-static inline struct passwd *getpw_str(const char *username, size_t len)\n+static struct passwd *getpw_str(const char *username, size_t len)\n {\n-\tif (len == 0)\n-\t\treturn getpwuid(getuid());\n-\n \tstruct passwd *pw;\n \tchar *username_z = xmalloc(len + 1);\n \tmemcpy(username_z, username, len);\n@@ -223,18 +220,18 @@ static inline struct passwd *getpw_str(const char *username, size_t len)\n }\n \n /*\n- * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL, then\n- * it is a newly allocated string. Returns NULL on getpw failure or if\n- * path is NULL.\n+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,\n+ * then it is a newly allocated string. Returns NULL on getpw failure or\n+ * if path is NULL.\n  */\n char *expand_user_path(const char *path)\n {\n \tstruct strbuf user_path = STRBUF_INIT;\n-\tchar * first_slash = strchrnul(path, '/');\n-\tchar * to_copy;\n+\tconst char *first_slash = strchrnul(path, '/');\n+\tconst char *to_copy = path;\n+\n \tif (path == NULL)\n \t\tgoto return_null;\n-\n \tif (path[0] == '~') {\n \t\tconst char *username = path + 1;\n \t\tsize_t username_len = first_slash - username;\n@@ -243,8 +240,6 @@ char *expand_user_path(const char *path)\n \t\t\tgoto return_null;\n \t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n \t\tto_copy = first_slash;\n-\t} else if (path[0] != '/') {\n-\t\tto_copy = path;\n \t}\n \tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n \treturn strbuf_detach(&user_path, NULL);\n@@ -300,14 +295,15 @@ char *enter_repo(char *path, int strict)\n \t\tif (path[0] == '~') {\n \t\t\tchar *newpath = expand_user_path(path);\n \t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n-\t\t\t\tif (path != newpath)\n-\t\t\t\t\tfree(newpath);\n+\t\t\t\tfree(newpath);\n \t\t\t\treturn NULL;\n \t\t\t}\n-\t\t\t/* Copy back into the static buffer. A pity\n-\t\t\t   since newpath was not bounded, but other\n-\t\t\t   branches of the if are limited by PATH_MAX\n-\t\t\t   anyway. */\n+\t\t\t/*\n+\t\t\t * Copy back into the static buffer. A pity\n+\t\t\t * since newpath was not bounded, but other\n+\t\t\t * branches of the if are limited by PATH_MAX\n+\t\t\t * anyway.\n+\t\t\t */\n \t\t\tstrcpy(used_path, newpath); free(newpath);\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\n-- \n1.6.5.3.283.g4b054\n"},{"id":"127743","messageId":"20091117073426.GB4007@coredump.intra.peff.net","threadId":"21635","inReplyTo":"1258366060-27966-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-11-17T07:34:26Z","receivedAt":"2009-11-17T07:34:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Nov 16, 2009 at 11:07:40AM +0100, Matthieu Moy wrote:\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index d1e2120..c37b51d 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -380,7 +380,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n>  core.excludesfile::\n>  \tIn addition to '.gitignore' (per-directory) and\n>  \t'.git/info/exclude', git looks into this file for patterns\n> -\tof files which are not meant to be tracked.  See\n> +\tof files which are not meant to be tracked.  \"~\" and \"~user\"\n> +\tare expanded to the user's home directory.  See\n>  \tlinkgit:gitignore[5].\n\nReading this, it is not clear to me if:\n\n  1. \"~\" and \"~user\" are expanded to the home directory of \"user\", where\n     \"user\" is the user running git\n\nor\n\n  2. \"~\" is expanded to the home directory of the user running git, and\n     an arbitrary \"~user\" is expanded to that user's home directory.\n\nI would expect (2), since that is how everything else works. And it\nseems from the code that is what you do. But something about the way it\nis written makes me think of (1). I also recall having this same\nquestion during the last round, so at the very least it is not me just\nmis-reading it once. ;)\n\nMaybe:\n\n  A leading path component of \"~user\" is expanded to the home directory\n  of \"user\"; \"~\" is expanded to the home directory of the user running\n  git.\n\nwould be more clear?\n\n-Peff\n"},{"id":"127748","messageId":"20091117074930.GB11636@glandium.org","threadId":"21635","inReplyTo":"20091117073426.GB4007@coredump.intra.peff.net","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2009-11-17T07:49:30Z","receivedAt":"2009-11-17T07:49:30Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Tue, Nov 17, 2009 at 02:34:26AM -0500, Jeff King wrote:\n> Maybe:\n> \n>   A leading path component of \"~user\" is expanded to the home directory\n>   of \"user\"; \"~\" is expanded to the home directory of the user running\n>   git.\n> \n> would be more clear?\n\nAdd \"real\" before \"user running git\" and you have my vote. Or maybe\nusing the effective user would be better, and the patch should use\ngeteuid() instead of getuid(), I don't know. ident.c uses getuid(), but\nI'm wondering if that's what it should use (although it doesn't seem to\nhave been a problem to anyone)\n\nMike\n"},{"id":"127752","messageId":"vpqpr7hpm5b.fsf@bauges.imag.fr","threadId":"21635","inReplyTo":"20091117073426.GB4007@coredump.intra.peff.net","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2009-11-17T08:53:52Z","receivedAt":"2009-11-17T08:53:52Z","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 Mon, Nov 16, 2009 at 11:07:40AM +0100, Matthieu Moy wrote:\n>\n>> +\tof files which are not meant to be tracked.  \"~\" and \"~user\"\n>> +\tare expanded to the user's home directory.  See\n>>  \tlinkgit:gitignore[5].\n>\n> Reading this, it is not clear to me if:\n>\n>   1. \"~\" and \"~user\" are expanded to the home directory of \"user\", where\n>      \"user\" is the user running git\n>\n> or\n>\n>   2. \"~\" is expanded to the home directory of the user running git, and\n>      an arbitrary \"~user\" is expanded to that user's home directory.\n>\n> I would expect (2), since that is how everything else works.\n\nYes. \"user\" in the sentence is either the user running Git or the same\nstring as \"user\" in \"~user\". I'm not against your proposal, but I'm\nafraid we're making the sentence uselessly heavy, since, as you say,\nthis ~ and ~user convention is widely spread, and I hardly imagine\nsomeone interpreting the sentence as \"if you say ~foo, it will expand\nto the home directory of bar\".\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"127753","messageId":"20091117085603.GB4928@coredump.intra.peff.net","threadId":"21635","inReplyTo":"vpqpr7hpm5b.fsf@bauges.imag.fr","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-11-17T08:56:03Z","receivedAt":"2009-11-17T08:56:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Nov 17, 2009 at 09:53:52AM +0100, Matthieu Moy wrote:\n\n> Yes. \"user\" in the sentence is either the user running Git or the same\n> string as \"user\" in \"~user\". I'm not against your proposal, but I'm\n> afraid we're making the sentence uselessly heavy, since, as you say,\n> this ~ and ~user convention is widely spread, and I hardly imagine\n> someone interpreting the sentence as \"if you say ~foo, it will expand\n> to the home directory of bar\".\n\nI didn't think it would expand ~foo to the home directory of bar. I\nthought that it might _only_ accept ~bar, and not ~foo.\n\nIf I'm the only one confused, then we can drop it. But if not, I don't\nthink it is much more work to be precise.\n\n-Peff\n"},{"id":"127754","messageId":"vpqlji5plyn.fsf@bauges.imag.fr","threadId":"21635","inReplyTo":"7viqd9isbm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2009-11-17T08:57:52Z","receivedAt":"2009-11-17T08:57:52Z","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> Jakub Narebski <jnareb@gmail.com> writes:\n>\n>> It would be nice to have an option to git-config which would do such\n>> expansion, as a separate type similar to --int and --bool, e.g.:\n>>\n>>   git config --path section.key\n>>\n>> so that not only core.excludesfile could use this new feature, but for\n>> example also core.worktree, commit.template, gitcvs.logfile,\n>> mailmap.file, and perhaps also *.receivepack and *.uploadpack\n>\n> What should \"git config -l\" do for these (and core.excludesfile)?\n\nI don't know what it \"should\", but it \"does\" not do the expansion. I\nhad the same questionning when testing the patch, I'd have liked to be\nable to write a simple test-case like\n\n$ git config core.excludesfile '~/foo'\n$ git config --i-dont-know-what core.excludesfile\n\nto go through this codepath. Maybe we can just say\n\n$ git config --default core.excludesfile\n\nto say \"call git_default_config(...) on this before printing it\". My\nunderstanding is that this is what the C code is doing, we should\nallow the shell scripts to do the same.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"127755","messageId":"vpqhbstplvu.fsf@bauges.imag.fr","threadId":"21635","inReplyTo":"7vy6m5hci0.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2009-11-17T08:59:33Z","receivedAt":"2009-11-17T08:59:33Z","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'd like to squash this to your patch, based on the earlier review\n> comments.\n\nSure. And apologize for the style issues, I knew it, but one hardly\nchanges his (bad) habits ;-).\n\nI'll resend soon.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"127756","messageId":"200911171030.38703.jnareb@gmail.com","threadId":"21635","inReplyTo":"7viqd9isbm.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-11-17T09:30:37Z","receivedAt":"2009-11-17T09:30:37Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia wtorek 17. listopada 2009 07:22, Junio C Hamano napisał:\n> Jakub Narebski <jnareb@gmail.com> writes:\n\n> >\n> > It would be nice to have an option to git-config which would do such\n> > expansion, as a separate type similar to --int and --bool, e.g.:\n> >\n> >   git config --path section.key\n> >\n> > so that not only core.excludesfile could use this new feature, but for\n> > example also core.worktree, commit.template, gitcvs.logfile,\n> > mailmap.file, and perhaps also *.receivepack and *.uploadpack\n> \n> What should \"git config -l\" do for these (and core.excludesfile)?\n\nNothing (i.e. show unexpanded / not converted), just like for boolean\nvariables \"git config -l\" doesn't convert 1/on/yes to true, and 0/off/no\nto false, just like for integer variables \"git config -l\" doesn't convert\nto simple decimal number taking into account optional 'k', 'm' or 'g'\nsuffix.\n\n\nBTW. the suffix part of integer conversion should really be described\nin the paragraph about --int and --bool options (which should be made\ninto proper description list, and not a prose paragraph).\n\nP.S. I am a bit missing --local / --repository option to git-config to\ncomplement --global (which should probably be named --user) and --system\noptions.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"127762","messageId":"200911171430.36227.jnareb@gmail.com","threadId":"21635","inReplyTo":"vpqlji5plyn.fsf@bauges.imag.fr","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-11-17T13:30:35Z","receivedAt":"2009-11-17T13:30:35Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia wtorek 17. listopada 2009 09:57, Matthieu Moy napisał:\n> Junio C Hamano <gitster@pobox.com> writes:\n>> Jakub Narebski <jnareb@gmail.com> writes:\n>>\n>>> It would be nice to have an option to git-config which would do such\n>>> expansion, as a separate type similar to --int and --bool, e.g.:\n>>>\n>>>   git config --path section.key\n>>>\n>>> so that not only core.excludesfile could use this new feature, but for\n>>> example also core.worktree, commit.template, gitcvs.logfile,\n>>> mailmap.file, and perhaps also *.receivepack and *.uploadpack\n>>\n>> What should \"git config -l\" do for these (and core.excludesfile)?\n> \n> I don't know what it \"should\", but it \"does\" not do the expansion. I\n> had the same questionning when testing the patch, I'd have liked to be\n> able to write a simple test-case like\n> \n> $ git config core.excludesfile '~/foo'\n> $ git config --i-dont-know-what core.excludesfile\n> \n> to go through this codepath. Maybe we can just say\n> \n> $ git config --default core.excludesfile\n> \n> to say \"call git_default_config(...) on this before printing it\". My\n> understanding is that this is what the C code is doing, we should\n> allow the shell scripts to do the same.\n\nI think it is a very good idea.  Nevertheless it can apply only to\nconfig variables git core knows about, and not for example for git-gui,\nor gitk, or qgit, or tig, or StGIT, etc. configuration.  Therefore\n\"git config --path\" would be still needed.\n\n-- \nJakub Narębski\nPoland\n"},{"id":"127765","messageId":"1258478665-8635-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"21635","inReplyTo":"1258366060-27966-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH v2] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-11-17T17:24:25Z","receivedAt":"2009-11-17T17:24:25Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"These config variables are parsed to substitute ~ and ~user with getpw\nentries.\n\nuser_path() refactored into new function expand_user_path(), to allow\ndynamically allocating the return buffer.\n\nOriginal patch by Karl Chen, modified by Matthieu Moy, and further\namended by Junio C Hamano.\n\nSigned-off-by: Karl Chen <quarl@quarl.org>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\nHere's the new version, with Junio's modifications squashed in.\n\nI just replaced \"user\" with \"specified user\" to make it clear one can\nspecify a user with ~foo/, but since the documentation appears in two\nplaces (possibly more in the future), I prefered to Keep It Simple.\n\n Documentation/config.txt |    4 ++-\n builtin-commit.c         |    2 +-\n cache.h                  |    2 +\n config.c                 |   12 ++++++-\n path.c                   |   80 ++++++++++++++++++++++++++-------------------\n 5 files changed, 63 insertions(+), 37 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d1e2120..475d544 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -380,7 +380,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n core.excludesfile::\n \tIn addition to '.gitignore' (per-directory) and\n \t'.git/info/exclude', git looks into this file for patterns\n-\tof files which are not meant to be tracked.  See\n+\tof files which are not meant to be tracked.  \"~/\" and \"~user/\"\n+\tare expanded to the specified user's home directory.  See\n \tlinkgit:gitignore[5].\n \n core.editor::\n@@ -670,6 +671,7 @@ color.ui::\n \n commit.template::\n \tSpecify a file to use as the template for new commit messages.\n+\t\"~/\" and \"~user/\" are expanded to the specified user's home directory.\n \n diff.autorefreshindex::\n \tWhen using 'git-diff' to compare with work tree\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex d525b89..09d2840 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -999,7 +999,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \tstruct wt_status *s = cb;\n \n \tif (!strcmp(k, \"commit.template\"))\n-\t\treturn git_config_string(&template_file, k, v);\n+\t\treturn git_config_pathname(&template_file, k, v);\n \n \treturn git_status_config(k, v, s);\n }\ndiff --git a/cache.h b/cache.h\nindex 71a731d..42f7cd8 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -645,6 +645,7 @@ int set_shared_perm(const char *path, int mode);\n #define adjust_shared_perm(path) set_shared_perm((path), 0)\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n+extern char *expand_user_path(const char *path);\n char *enter_repo(char *path, int strict);\n static inline int is_absolute_path(const char *path)\n {\n@@ -903,6 +904,7 @@ extern unsigned long git_config_ulong(const char *, const char *);\n extern int git_config_bool_or_int(const char *, const char *, int *);\n extern int git_config_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n+extern int git_config_pathname(const char **, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_set_multivar(const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex c644061..b3d1ff4 100644\n--- a/config.c\n+++ b/config.c\n@@ -351,6 +351,16 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n+int git_config_pathname(const char **dest, const char *var, const char *value)\n+{\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\t*dest = expand_user_path(value);\n+\tif (!*dest)\n+\t\tdie(\"Failed to expand user dir in: '%s'\", value);\n+\treturn 0;\n+}\n+\n static int git_default_core_config(const char *var, const char *value)\n {\n \t/* This needs a better name */\n@@ -474,7 +484,7 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn git_config_string(&editor_program, var, value);\n \n \tif (!strcmp(var, \"core.excludesfile\"))\n-\t\treturn git_config_string(&excludes_file, var, value);\n+\t\treturn git_config_pathname(&excludes_file, var, value);\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n \t\tif (!value)\ndiff --git a/path.c b/path.c\nindex 047fdb0..2470f78 100644\n--- a/path.c\n+++ b/path.c\n@@ -11,6 +11,7 @@\n  * which is what it's designed for.\n  */\n #include \"cache.h\"\n+#include \"strbuf.h\"\n \n static char bad_path[] = \"/bad-path/\";\n \n@@ -207,43 +208,44 @@ int validate_headref(const char *path)\n \treturn -1;\n }\n \n-static char *user_path(char *buf, char *path, int sz)\n+static struct passwd *getpw_str(const char *username, size_t len)\n {\n \tstruct passwd *pw;\n-\tchar *slash;\n-\tint len, baselen;\n+\tchar *username_z = xmalloc(len + 1);\n+\tmemcpy(username_z, username, len);\n+\tusername_z[len] = '\\0';\n+\tpw = getpwnam(username_z);\n+\tfree(username_z);\n+\treturn pw;\n+}\n \n-\tif (!path || path[0] != '~')\n-\t\treturn NULL;\n-\tpath++;\n-\tslash = strchr(path, '/');\n-\tif (path[0] == '/' || !path[0]) {\n-\t\tpw = getpwuid(getuid());\n-\t}\n-\telse {\n-\t\tif (slash) {\n-\t\t\t*slash = 0;\n-\t\t\tpw = getpwnam(path);\n-\t\t\t*slash = '/';\n-\t\t}\n-\t\telse\n-\t\t\tpw = getpwnam(path);\n-\t}\n-\tif (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))\n-\t\treturn NULL;\n-\tbaselen = strlen(pw->pw_dir);\n-\tmemcpy(buf, pw->pw_dir, baselen);\n-\twhile ((1 < baselen) && (buf[baselen-1] == '/')) {\n-\t\tbuf[baselen-1] = 0;\n-\t\tbaselen--;\n-\t}\n-\tif (slash && slash[1]) {\n-\t\tlen = strlen(slash);\n-\t\tif (sz <= baselen + len)\n-\t\t\treturn NULL;\n-\t\tmemcpy(buf + baselen, slash, len + 1);\n+/*\n+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,\n+ * then it is a newly allocated string. Returns NULL on getpw failure or\n+ * if path is NULL.\n+ */\n+char *expand_user_path(const char *path)\n+{\n+\tstruct strbuf user_path = STRBUF_INIT;\n+\tconst char *first_slash = strchrnul(path, '/');\n+\tconst char *to_copy = path;\n+\n+\tif (path == NULL)\n+\t\tgoto return_null;\n+\tif (path[0] == '~') {\n+\t\tconst char *username = path + 1;\n+\t\tsize_t username_len = first_slash - username;\n+\t\tstruct passwd *pw = getpw_str(username, username_len);\n+\t\tif (!pw)\n+\t\t\tgoto return_null;\n+\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n+\t\tto_copy = first_slash;\n \t}\n-\treturn buf;\n+\tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n+\treturn strbuf_detach(&user_path, NULL);\n+return_null:\n+\tstrbuf_release(&user_path);\n+\treturn NULL;\n }\n \n /*\n@@ -291,8 +293,18 @@ char *enter_repo(char *path, int strict)\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n \t\tif (path[0] == '~') {\n-\t\t\tif (!user_path(used_path, path, PATH_MAX))\n+\t\t\tchar *newpath = expand_user_path(path);\n+\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n+\t\t\t\tfree(newpath);\n \t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * Copy back into the static buffer. A pity\n+\t\t\t * since newpath was not bounded, but other\n+\t\t\t * branches of the if are limited by PATH_MAX\n+\t\t\t * anyway.\n+\t\t\t */\n+\t\t\tstrcpy(used_path, newpath); free(newpath);\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\n \t\t}\n-- \n1.6.5.2.152.gbbe9e\n"},{"id":"127775","messageId":"m2lji43l20.fsf@igel.home","threadId":"21635","inReplyTo":"20091117074930.GB11636@glandium.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2009-11-17T21:20:39Z","receivedAt":"2009-11-17T21:20:39Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> On Tue, Nov 17, 2009 at 02:34:26AM -0500, Jeff King wrote:\n>> Maybe:\n>> \n>>   A leading path component of \"~user\" is expanded to the home directory\n>>   of \"user\"; \"~\" is expanded to the home directory of the user running\n>>   git.\n>> \n>> would be more clear?\n>\n> Add \"real\" before \"user running git\" and you have my vote. Or maybe\n> using the effective user would be better, and the patch should use\n> geteuid() instead of getuid(), I don't know. ident.c uses getuid(), but\n> I'm wondering if that's what it should use (although it doesn't seem to\n> have been a problem to anyone)\n\n\"~\" should just expand to the value of $HOME, like in the shell,\nindependent of the real home directory of the real or effective user.\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"127778","messageId":"7v8we4er0t.fsf@alter.siamese.dyndns.org","threadId":"21635","inReplyTo":"m2lji43l20.fsf@igel.home","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-17T22:16:18Z","receivedAt":"2009-11-17T22:16:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Schwab <schwab@linux-m68k.org> writes:\n\n> Mike Hommey <mh@glandium.org> writes:\n>\n>> On Tue, Nov 17, 2009 at 02:34:26AM -0500, Jeff King wrote:\n>>> Maybe:\n>>> \n>>>   A leading path component of \"~user\" is expanded to the home directory\n>>>   of \"user\"; \"~\" is expanded to the home directory of the user running\n>>>   git.\n>>> \n>>> would be more clear?\n>>\n>> Add \"real\" before \"user running git\" and you have my vote. Or maybe\n>> using the effective user would be better, and the patch should use\n>> geteuid() instead of getuid(), I don't know. ident.c uses getuid(), but\n>> I'm wondering if that's what it should use (although it doesn't seem to\n>> have been a problem to anyone)\n>\n> \"~\" should just expand to the value of $HOME, like in the shell,\n> independent of the real home directory of the real or effective user.\n\nHow should this interact with installations that run gitosis/gitlite that\nuse shared account, giving user identity via the ssh key?\n\nNote that the question is not \"how would this...\", but \"how _should_\nthis...\".\n"},{"id":"127793","messageId":"m2bpj03bpn.fsf@igel.home","threadId":"21635","inReplyTo":"7v8we4er0t.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2009-11-18T00:42:28Z","receivedAt":"2009-11-18T00:42:28Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Andreas Schwab <schwab@linux-m68k.org> writes:\n>\n>> Mike Hommey <mh@glandium.org> writes:\n>>\n>>> On Tue, Nov 17, 2009 at 02:34:26AM -0500, Jeff King wrote:\n>>>> Maybe:\n>>>> \n>>>>   A leading path component of \"~user\" is expanded to the home directory\n>>>>   of \"user\"; \"~\" is expanded to the home directory of the user running\n>>>>   git.\n>>>> \n>>>> would be more clear?\n>>>\n>>> Add \"real\" before \"user running git\" and you have my vote. Or maybe\n>>> using the effective user would be better, and the patch should use\n>>> geteuid() instead of getuid(), I don't know. ident.c uses getuid(), but\n>>> I'm wondering if that's what it should use (although it doesn't seem to\n>>> have been a problem to anyone)\n>>\n>> \"~\" should just expand to the value of $HOME, like in the shell,\n>> independent of the real home directory of the real or effective user.\n>\n> How should this interact with installations that run gitosis/gitlite that\n> use shared account, giving user identity via the ssh key?\n\nSorry, I don't know enough about such an installation to be able to\nanswer that.\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"127823","messageId":"vpq7hto70sw.fsf@bauges.imag.fr","threadId":"21635","inReplyTo":"7v8we4er0t.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2009-11-18T07:24:31Z","receivedAt":"2009-11-18T07:24:31Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"> Andreas Schwab <schwab@linux-m68k.org> writes:\n>\n>> Mike Hommey <mh@glandium.org> writes:\n>>\n>> \"~\" should just expand to the value of $HOME, like in the shell,\n>> independent of the real home directory of the real or effective user.\n\nRight, I'll resend in a minute.\n\nJunio C Hamano <gitster@pobox.com> writes:\n\n> How should this interact with installations that run gitosis/gitlite that\n> use shared account, giving user identity via the ssh key?\n\nI guess there's some kind of rhetorical question behind that I missed,\nbut if a gitosis-like installation uses the user git, and the user foo\nconnects to it identifier by its SSH key, I don't see what you could\ndo other than expanding ~ to the homedir of git, which is $HOME at the\ntime your run git on the server.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"127822","messageId":"1258529351-4045-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"21635","inReplyTo":"1258478665-8635-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH v3] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-11-18T07:29:11Z","receivedAt":"2009-11-18T07:29:11Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"These config variables are parsed to substitute ~ and ~user with getpw\nentries.\n\nuser_path() refactored into new function expand_user_path(), to allow\ndynamically allocating the return buffer.\n\nOriginal patch by Karl Chen, modified by Matthieu Moy, and further\namended by Junio C Hamano.\n\nSigned-off-by: Karl Chen <quarl@quarl.org>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n\nSince v2, I changed the code to use $HOME instead of the actual home\nto expand ~.\n\nThis way, if you run \"HOME=/else/where/ git status\", then both\n.gitconfig and ~/.gitignore will be taken from /else/where.\n\n Documentation/config.txt |    7 +++-\n builtin-commit.c         |    2 +-\n cache.h                  |    2 +\n config.c                 |   12 ++++++-\n path.c                   |   83 +++++++++++++++++++++++++++------------------\n 5 files changed, 69 insertions(+), 37 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex cb73d75..fb1740c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -380,8 +380,9 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n core.excludesfile::\n \tIn addition to '.gitignore' (per-directory) and\n \t'.git/info/exclude', git looks into this file for patterns\n-\tof files which are not meant to be tracked.  See\n-\tlinkgit:gitignore[5].\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 \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\n@@ -670,6 +671,8 @@ color.ui::\n \n commit.template::\n \tSpecify a file to use as the template for new commit messages.\n+\t\"~/\" is expanded to the value of `$HOME` and \"~user/\" to the\n+\tspecified user's home directory.\n \n diff.autorefreshindex::\n \tWhen using 'git-diff' to compare with work tree\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex d525b89..09d2840 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -999,7 +999,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \tstruct wt_status *s = cb;\n \n \tif (!strcmp(k, \"commit.template\"))\n-\t\treturn git_config_string(&template_file, k, v);\n+\t\treturn git_config_pathname(&template_file, k, v);\n \n \treturn git_status_config(k, v, s);\n }\ndiff --git a/cache.h b/cache.h\nindex 71a731d..42f7cd8 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -645,6 +645,7 @@ int set_shared_perm(const char *path, int mode);\n #define adjust_shared_perm(path) set_shared_perm((path), 0)\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n+extern char *expand_user_path(const char *path);\n char *enter_repo(char *path, int strict);\n static inline int is_absolute_path(const char *path)\n {\n@@ -903,6 +904,7 @@ extern unsigned long git_config_ulong(const char *, const char *);\n extern int git_config_bool_or_int(const char *, const char *, int *);\n extern int git_config_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n+extern int git_config_pathname(const char **, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_set_multivar(const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex c644061..b3d1ff4 100644\n--- a/config.c\n+++ b/config.c\n@@ -351,6 +351,16 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n+int git_config_pathname(const char **dest, const char *var, const char *value)\n+{\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\t*dest = expand_user_path(value);\n+\tif (!*dest)\n+\t\tdie(\"Failed to expand user dir in: '%s'\", value);\n+\treturn 0;\n+}\n+\n static int git_default_core_config(const char *var, const char *value)\n {\n \t/* This needs a better name */\n@@ -474,7 +484,7 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn git_config_string(&editor_program, var, value);\n \n \tif (!strcmp(var, \"core.excludesfile\"))\n-\t\treturn git_config_string(&excludes_file, var, value);\n+\t\treturn git_config_pathname(&excludes_file, var, value);\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n \t\tif (!value)\ndiff --git a/path.c b/path.c\nindex 047fdb0..6351b57 100644\n--- a/path.c\n+++ b/path.c\n@@ -11,6 +11,7 @@\n  * which is what it's designed for.\n  */\n #include \"cache.h\"\n+#include \"strbuf.h\"\n \n static char bad_path[] = \"/bad-path/\";\n \n@@ -207,43 +208,49 @@ int validate_headref(const char *path)\n \treturn -1;\n }\n \n-static char *user_path(char *buf, char *path, int sz)\n+static struct passwd *getpw_str(const char *username, size_t len)\n {\n \tstruct passwd *pw;\n-\tchar *slash;\n-\tint len, baselen;\n+\tchar *username_z = xmalloc(len + 1);\n+\tmemcpy(username_z, username, len);\n+\tusername_z[len] = '\\0';\n+\tpw = getpwnam(username_z);\n+\tfree(username_z);\n+\treturn pw;\n+}\n \n-\tif (!path || path[0] != '~')\n-\t\treturn NULL;\n-\tpath++;\n-\tslash = strchr(path, '/');\n-\tif (path[0] == '/' || !path[0]) {\n-\t\tpw = getpwuid(getuid());\n-\t}\n-\telse {\n-\t\tif (slash) {\n-\t\t\t*slash = 0;\n-\t\t\tpw = getpwnam(path);\n-\t\t\t*slash = '/';\n+/*\n+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,\n+ * then it is a newly allocated string. Returns NULL on getpw failure or\n+ * if path is NULL.\n+ */\n+char *expand_user_path(const char *path)\n+{\n+\tstruct strbuf user_path = STRBUF_INIT;\n+\tconst char *first_slash = strchrnul(path, '/');\n+\tconst char *to_copy = path;\n+\n+\tif (path == NULL)\n+\t\tgoto return_null;\n+\tif (path[0] == '~') {\n+\t\tconst char *username = path + 1;\n+\t\tsize_t username_len = first_slash - username;\n+\t\tif (username_len == 0) {\n+\t\t\tconst char * home = getenv(\"HOME\");\n+\t\t\tstrbuf_add(&user_path, home, strlen(home));\n+\t\t} else {\n+\t\t\tstruct passwd *pw = getpw_str(username, username_len);\n+\t\t\tif (!pw)\n+\t\t\t\tgoto return_null;\n+\t\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n \t\t}\n-\t\telse\n-\t\t\tpw = getpwnam(path);\n+\t\tto_copy = first_slash;\n \t}\n-\tif (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))\n-\t\treturn NULL;\n-\tbaselen = strlen(pw->pw_dir);\n-\tmemcpy(buf, pw->pw_dir, baselen);\n-\twhile ((1 < baselen) && (buf[baselen-1] == '/')) {\n-\t\tbuf[baselen-1] = 0;\n-\t\tbaselen--;\n-\t}\n-\tif (slash && slash[1]) {\n-\t\tlen = strlen(slash);\n-\t\tif (sz <= baselen + len)\n-\t\t\treturn NULL;\n-\t\tmemcpy(buf + baselen, slash, len + 1);\n-\t}\n-\treturn buf;\n+\tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n+\treturn strbuf_detach(&user_path, NULL);\n+return_null:\n+\tstrbuf_release(&user_path);\n+\treturn NULL;\n }\n \n /*\n@@ -291,8 +298,18 @@ char *enter_repo(char *path, int strict)\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n \t\tif (path[0] == '~') {\n-\t\t\tif (!user_path(used_path, path, PATH_MAX))\n+\t\t\tchar *newpath = expand_user_path(path);\n+\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n+\t\t\t\tfree(newpath);\n \t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * Copy back into the static buffer. A pity\n+\t\t\t * since newpath was not bounded, but other\n+\t\t\t * branches of the if are limited by PATH_MAX\n+\t\t\t * anyway.\n+\t\t\t */\n+\t\t\tstrcpy(used_path, newpath); free(newpath);\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\n \t\t}\n-- \n1.6.5.2.152.gbbe9e\n"},{"id":"127830","messageId":"1258534693-7220-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"21635","inReplyTo":"1258529351-4045-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH v4] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-11-18T08:58:13Z","receivedAt":"2009-11-18T08:58:13Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"These config variables are parsed to substitute ~ and ~user with getpw\nentries.\n\nuser_path() refactored into new function expand_user_path(), to allow\ndynamically allocating the return buffer.\n\nOriginal patch by Karl Chen, modified by Matthieu Moy, and further\namended by Junio C Hamano.\n\nSigned-off-by: Karl Chen <quarl@quarl.org>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n\"Oops, I did it again :-(\"\n\nJust a style issue since v3 (char * home -> char *home), sorry for the noise.\n\n Documentation/config.txt |    7 +++-\n builtin-commit.c         |    2 +-\n cache.h                  |    2 +\n config.c                 |   12 ++++++-\n path.c                   |   83 +++++++++++++++++++++++++++------------------\n 5 files changed, 69 insertions(+), 37 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 78ee906..b4fe843 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -380,8 +380,9 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n core.excludesfile::\n \tIn addition to '.gitignore' (per-directory) and\n \t'.git/info/exclude', git looks into this file for patterns\n-\tof files which are not meant to be tracked.  See\n-\tlinkgit:gitignore[5].\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 \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\n@@ -681,6 +682,8 @@ color.ui::\n \n commit.template::\n \tSpecify a file to use as the template for new commit messages.\n+\t\"~/\" is expanded to the value of `$HOME` and \"~user/\" to the\n+\tspecified user's home directory.\n \n diff.autorefreshindex::\n \tWhen using 'git-diff' to compare with work tree\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 0fd7af6..87d1b90 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -1175,7 +1175,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \tstruct wt_status *s = cb;\n \n \tif (!strcmp(k, \"commit.template\"))\n-\t\treturn git_config_string(&template_file, k, v);\n+\t\treturn git_config_pathname(&template_file, k, v);\n \n \treturn git_status_config(k, v, s);\n }\ndiff --git a/cache.h b/cache.h\nindex f7533ff..2ba9690 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -649,6 +649,7 @@ int set_shared_perm(const char *path, int mode);\n #define adjust_shared_perm(path) set_shared_perm((path), 0)\n int safe_create_leading_directories(char *path);\n int safe_create_leading_directories_const(const char *path);\n+extern char *expand_user_path(const char *path);\n char *enter_repo(char *path, int strict);\n static inline int is_absolute_path(const char *path)\n {\n@@ -909,6 +910,7 @@ extern unsigned long git_config_ulong(const char *, const char *);\n extern int git_config_bool_or_int(const char *, const char *, int *);\n extern int git_config_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n+extern int git_config_pathname(const char **, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_set_multivar(const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex 51f2208..37385ce 100644\n--- a/config.c\n+++ b/config.c\n@@ -351,6 +351,16 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n+int git_config_pathname(const char **dest, const char *var, const char *value)\n+{\n+\tif (!value)\n+\t\treturn config_error_nonbool(var);\n+\t*dest = expand_user_path(value);\n+\tif (!*dest)\n+\t\tdie(\"Failed to expand user dir in: '%s'\", value);\n+\treturn 0;\n+}\n+\n static int git_default_core_config(const char *var, const char *value)\n {\n \t/* This needs a better name */\n@@ -479,7 +489,7 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn git_config_string(&editor_program, var, value);\n \n \tif (!strcmp(var, \"core.excludesfile\"))\n-\t\treturn git_config_string(&excludes_file, var, value);\n+\t\treturn git_config_pathname(&excludes_file, var, value);\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n \t\tif (!value)\ndiff --git a/path.c b/path.c\nindex c7679be..2ec950b 100644\n--- a/path.c\n+++ b/path.c\n@@ -11,6 +11,7 @@\n  * which is what it's designed for.\n  */\n #include \"cache.h\"\n+#include \"strbuf.h\"\n \n static char bad_path[] = \"/bad-path/\";\n \n@@ -207,43 +208,49 @@ int validate_headref(const char *path)\n \treturn -1;\n }\n \n-static char *user_path(char *buf, char *path, int sz)\n+static struct passwd *getpw_str(const char *username, size_t len)\n {\n \tstruct passwd *pw;\n-\tchar *slash;\n-\tint len, baselen;\n+\tchar *username_z = xmalloc(len + 1);\n+\tmemcpy(username_z, username, len);\n+\tusername_z[len] = '\\0';\n+\tpw = getpwnam(username_z);\n+\tfree(username_z);\n+\treturn pw;\n+}\n \n-\tif (!path || path[0] != '~')\n-\t\treturn NULL;\n-\tpath++;\n-\tslash = strchr(path, '/');\n-\tif (path[0] == '/' || !path[0]) {\n-\t\tpw = getpwuid(getuid());\n-\t}\n-\telse {\n-\t\tif (slash) {\n-\t\t\t*slash = 0;\n-\t\t\tpw = getpwnam(path);\n-\t\t\t*slash = '/';\n+/*\n+ * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,\n+ * then it is a newly allocated string. Returns NULL on getpw failure or\n+ * if path is NULL.\n+ */\n+char *expand_user_path(const char *path)\n+{\n+\tstruct strbuf user_path = STRBUF_INIT;\n+\tconst char *first_slash = strchrnul(path, '/');\n+\tconst char *to_copy = path;\n+\n+\tif (path == NULL)\n+\t\tgoto return_null;\n+\tif (path[0] == '~') {\n+\t\tconst char *username = path + 1;\n+\t\tsize_t username_len = first_slash - username;\n+\t\tif (username_len == 0) {\n+\t\t\tconst char *home = getenv(\"HOME\");\n+\t\t\tstrbuf_add(&user_path, home, strlen(home));\n+\t\t} else {\n+\t\t\tstruct passwd *pw = getpw_str(username, username_len);\n+\t\t\tif (!pw)\n+\t\t\t\tgoto return_null;\n+\t\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n \t\t}\n-\t\telse\n-\t\t\tpw = getpwnam(path);\n+\t\tto_copy = first_slash;\n \t}\n-\tif (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))\n-\t\treturn NULL;\n-\tbaselen = strlen(pw->pw_dir);\n-\tmemcpy(buf, pw->pw_dir, baselen);\n-\twhile ((1 < baselen) && (buf[baselen-1] == '/')) {\n-\t\tbuf[baselen-1] = 0;\n-\t\tbaselen--;\n-\t}\n-\tif (slash && slash[1]) {\n-\t\tlen = strlen(slash);\n-\t\tif (sz <= baselen + len)\n-\t\t\treturn NULL;\n-\t\tmemcpy(buf + baselen, slash, len + 1);\n-\t}\n-\treturn buf;\n+\tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n+\treturn strbuf_detach(&user_path, NULL);\n+return_null:\n+\tstrbuf_release(&user_path);\n+\treturn NULL;\n }\n \n /*\n@@ -291,8 +298,18 @@ char *enter_repo(char *path, int strict)\n \t\tif (PATH_MAX <= len)\n \t\t\treturn NULL;\n \t\tif (path[0] == '~') {\n-\t\t\tif (!user_path(used_path, path, PATH_MAX))\n+\t\t\tchar *newpath = expand_user_path(path);\n+\t\t\tif (!newpath || (PATH_MAX - 10 < strlen(newpath))) {\n+\t\t\t\tfree(newpath);\n \t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * Copy back into the static buffer. A pity\n+\t\t\t * since newpath was not bounded, but other\n+\t\t\t * branches of the if are limited by PATH_MAX\n+\t\t\t * anyway.\n+\t\t\t */\n+\t\t\tstrcpy(used_path, newpath); free(newpath);\n \t\t\tstrcpy(validated_path, path);\n \t\t\tpath = used_path;\n \t\t}\n-- \n1.6.5.2.152.gbbe9e\n"},{"id":"127905","messageId":"1258644075-16191-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"21635","inReplyTo":"1258534693-7220-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH] expand_user_path: expand ~ to $HOME, not to the actual homedir.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-11-19T15:21:15Z","receivedAt":"2009-11-19T15:21:15Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"In 395de250d (Expand ~ and ~user in core.excludesfile, commit.template),\nwe introduced the mechanism. But expanding ~ using getpw is not what\npeople overriding $HOME would usually expect. In particular, git looks\nfor the user's .gitconfig using $HOME, so it's better to be consistent.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\nThis is basically my patch v4, but since an earlier version is already\nin next, I resend the last bits of it, based on next.\n\n Documentation/config.txt |    9 +++++----\n path.c                   |   13 +++++++++----\n 2 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 468e285..39d1226 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -381,9 +381,9 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.\n core.excludesfile::\n \tIn addition to '.gitignore' (per-directory) and\n \t'.git/info/exclude', git looks into this file for patterns\n-\tof files which are not meant to be tracked.  \"~/\" and \"~user/\"\n-\tare expanded to the specified user's home directory.  See\n-\tlinkgit:gitignore[5].\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 \n core.editor::\n \tCommands such as `commit` and `tag` that lets you edit\n@@ -683,7 +683,8 @@ color.ui::\n \n commit.template::\n \tSpecify a file to use as the template for new commit messages.\n-\t\"~/\" and \"~user/\" are expanded to the specified user's home directory.\n+\t\"~/\" is expanded to the value of `$HOME` and \"~user/\" to the\n+\tspecified user's home directory.\n \n diff.autorefreshindex::\n \tWhen using 'git-diff' to compare with work tree\ndiff --git a/path.c b/path.c\nindex b4a8075..2ec950b 100644\n--- a/path.c\n+++ b/path.c\n@@ -235,10 +235,15 @@ char *expand_user_path(const char *path)\n \tif (path[0] == '~') {\n \t\tconst char *username = path + 1;\n \t\tsize_t username_len = first_slash - username;\n-\t\tstruct passwd *pw = getpw_str(username, username_len);\n-\t\tif (!pw)\n-\t\t\tgoto return_null;\n-\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n+\t\tif (username_len == 0) {\n+\t\t\tconst char *home = getenv(\"HOME\");\n+\t\t\tstrbuf_add(&user_path, home, strlen(home));\n+\t\t} else {\n+\t\t\tstruct passwd *pw = getpw_str(username, username_len);\n+\t\t\tif (!pw)\n+\t\t\t\tgoto return_null;\n+\t\t\tstrbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));\n+\t\t}\n \t\tto_copy = first_slash;\n \t}\n \tstrbuf_add(&user_path, to_copy, strlen(to_copy));\n-- \n1.6.5.2.152.gbbe9e\n"},{"id":"127907","messageId":"20091119152359.GB6877@coredump.intra.peff.net","threadId":"21635","inReplyTo":"1258644075-16191-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH] expand_user_path: expand ~ to $HOME, not to the actual homedir.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-11-19T15:23:59Z","receivedAt":"2009-11-19T15:23:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 19, 2009 at 04:21:15PM +0100, Matthieu Moy wrote:\n\n> -\tof files which are not meant to be tracked.  \"~/\" and \"~user/\"\n> -\tare expanded to the specified user's home directory.  See\n> -\tlinkgit:gitignore[5].\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\nThanks. As a side effect, this wording change addresses my original\nambiguity concern (and I also think using $HOME is the right thing to\ndo).\n\n-Peff\n"},{"id":"127909","messageId":"vpq7htma30n.fsf@bauges.imag.fr","threadId":"21635","inReplyTo":"20091119152359.GB6877@coredump.intra.peff.net","subject":"Re: [PATCH] expand_user_path: expand ~ to $HOME, not to the actual homedir.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2009-11-19T16:32:56Z","receivedAt":"2009-11-19T16:32:56Z","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 Thu, Nov 19, 2009 at 04:21:15PM +0100, Matthieu Moy wrote:\n>\n>> -\tof files which are not meant to be tracked.  \"~/\" and \"~user/\"\n>> -\tare expanded to the specified user's home directory.  See\n>> -\tlinkgit:gitignore[5].\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>\n> Thanks. As a side effect, this wording change addresses my original\n> ambiguity concern (and I also think using $HOME is the right thing to\n> do).\n\nNot a pure coincidence indeed ;-). In this new version, ~ and ~foo do\nactually different things so they definitely deserve the heaviness of\na few extra words.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"127914","messageId":"7v7htmbcyh.fsf@alter.siamese.dyndns.org","threadId":"21635","inReplyTo":"1258534693-7220-1-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH v4] Expand ~ and ~user in core.excludesfile, commit.template","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-11-19T18:12:54Z","receivedAt":"2009-11-19T18:12:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Oops; last night I wrote\n\n    Thanks; v3 was already in next so I'll make an interdiff into a separate\n    \"fix-up\" commit.\n\nbut didn't send it out.  Thanks for rerolling the patch.\n"}]}