{"thread":{"id":"38752","subject":"[PATCH v2 1/4] git-credential-store: support multiple credential files","startedAt":"2015-03-08T07:58:48Z","lastAt":"2015-03-10T13:55:33Z","messageCount":8,"participants":["Paul Tan","Matthieu Moy"],"isPatch":true,"patchVersion":2,"patchTotal":4},"messages":[{"id":"257276","messageId":"1425801532-8483-1-git-send-email-pyokagan@gmail.com","threadId":"38752","inReplyTo":null,"subject":"[PATCH v2 0/4] git-credential-store: XDG user-specific config file support","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-08T07:58:48Z","receivedAt":"2015-03-08T07:58:48Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"The previous patch series can be found at [1].\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/264682\n\nThe changes are as follows:\n\n* Code refactor: instead of a gigantic blob of code in main(),\n  store_credential(), remove_credential() and lookup_credential() have been\n  modified to take a string_list of precedence-ordered file paths. Although in\n  this patch only support for XDG_CONFIG_HOME (user-specific config files) are\n  implemented, this opens the door for support of XDG_CONFIG_DIRS (system-wide\n  config files) to be implemented as well.\n\n* parse_credential_file() returns the value of found_credential at all times.\n  (Thanks Junio for pointing this out)\n\n* parse_credential_file(), and thus \"get\" ignores unreadable/non-existing files\n  instead of warning the user. This is done to follow the XDG base dir spec,\n  which states that: \"if for any reason a file in a certain directory is\n  unaccessible, e.g. because the directory is non-existant, the file is\n  non-existant or the user is not authorized to open the file, then the\n  processing of the file in that directory should be skipped.\"[2]\n\n[2] http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html\n\n* \"store\" now only write to a single file instead of writing to one and erasing\n  from the rest, unlike the behavior in the 1st patch, as the complexity\n  introduced by implementing this behavior is probably unnecessary. If the user\n  really wanted all matching credentials to be erased, the user can simply just\n  issue an erase. (Thanks Matthieu, Junio and Jeff for this suggestion)\n\n* ~/.git-credentials now has greater precedence than the xdg credentials file.\n  This is to be consistent with the behavior of git-config.\n\n* Support for $XDG_CONFIG_HOME/git/credentials has been documented in\n  git-credentials-store.\n\nThe changes for the tests are as follows:\n\n* Instead of repeating ${XDG_CONFIG_HOME:-$HOME/.config} all over\n  the place, add tests that test for $HOME/.config/git/credentials (when\n  XDG_CONFIG_HOME is unset) and for $XDG_CONFIG_HOME/git/credentials with a\n  custom $XDG_CONFIG_HOME directory set. This is to test that the new code\n  respects the $XDG_CONFIG_HOME environment variable. (Thanks Junio)\n\n* Use test_path_is_missing to test if files exist. (Thanks Matthieu)\n\n* All code is now within test_expect_success and will now fail if any\n  error occurs in the code block through the use of \"&&\". Furthermore, tests do\n  not rely on previous tests passing as the credential files are overwritten in\n  each test. (Thanks Matthieu and Junio for your code review)\n\nThe most current version of the patch queue is published in the xdg branch\nat [3]. I try to push -f regularly.\n\nFor your feedback, please.\n\n[3] https://github.com/pyokagan/git\n\nPaul Tan (4):\n  git-credential-store: support multiple credential files\n  git-credential-store: support XDG_CONFIG_HOME\n  docs/git-credential-store: document XDG file and precedence\n  t0302: test credential-store support for XDG_CONFIG_HOME\n\n Documentation/git-credential-store.txt | 37 +++++++++++++-\n credential-store.c                     | 86 +++++++++++++++++++++----------\n t/t0302-credential-store.sh            | 92 ++++++++++++++++++++++++++++++++++\n 3 files changed, 186 insertions(+), 29 deletions(-)\n\n-- \n2.1.4\n"},{"id":"257272","messageId":"1425801532-8483-2-git-send-email-pyokagan@gmail.com","threadId":"38752","inReplyTo":"1425801532-8483-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v2 1/4] git-credential-store: support multiple credential files","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-08T07:58:49Z","receivedAt":"2015-03-08T07:58:49Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Previously, git-credential-store only supported storing credentials in a\nsingle file: ~/.git-credentials. In order to support the XDG base\ndirectory specification[1], git-credential-store needs to be able to\nlookup and erase credentials from multiple files, as well as to pick the\nappropriate file to write to so that the credentials can be found on\nsubsequent lookups.\n\n[1] http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html\n\nNote that some credential storage files may not be owned, readable or\nwritable by the user, as they may be system-wide files that are meant to\napply to every user.\n\nInstead of a single file path, lookup_credential(), remove_credential()\nand store_credential() now take a precedence-ordered string_list of\nfile paths. lookup_credential() expects both user-specific and\nsystem-wide credential files to be provided to support the use case of\nsystem administrators setting default credentials for users.\nremove_credential() and store_credential() expect only the user-specific\ncredential files to be provided as usually the only config files that\nusers are allowed to edit are their own user-specific ones.\n\nlookup_credential() will read these (user-specific and system-wide) file\npaths in order until it finds the 1st matching credential and print it.\nAs some files may be private and thus unreadable, any file which cannot\nbe read will be ignored silently.\n\nremove_credential() will erase credentials from all (user-specific)\nfiles in the list.  This is because if credentials are only erased from\nthe file with the highest precedence, a matching credential may still be\nfound in a file further down the list. (Note that due to the lockfile\ncode, this requires the directory to be writable, which should be so for\nuser-specific config files)\n\nstore_credential() will write the credentials to the first existing\n(user-specific) file in the list. If none of the files in the list\nexist, store_credential() will write to the filename specified by\ndefault_index, thus creating it. For backwards compatibility,\n~/.git-credentials should be the file specified by default_index.\n\nHelped-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n credential-store.c | 77 ++++++++++++++++++++++++++++++++++++------------------\n 1 file changed, 52 insertions(+), 25 deletions(-)\n\ndiff --git a/credential-store.c b/credential-store.c\nindex 925d3f4..3455d7a 100644\n--- a/credential-store.c\n+++ b/credential-store.c\n@@ -6,7 +6,7 @@\n \n static struct lock_file credential_lock;\n \n-static void parse_credential_file(const char *fn,\n+static int parse_credential_file(const char *fn,\n \t\t\t\t  struct credential *c,\n \t\t\t\t  void (*match_cb)(struct credential *),\n \t\t\t\t  void (*other_cb)(struct strbuf *))\n@@ -14,18 +14,20 @@ static void parse_credential_file(const char *fn,\n \tFILE *fh;\n \tstruct strbuf line = STRBUF_INIT;\n \tstruct credential entry = CREDENTIAL_INIT;\n+\tint found_credential = 0;\n \n \tfh = fopen(fn, \"r\");\n \tif (!fh) {\n-\t\tif (errno != ENOENT)\n+\t\tif (errno != ENOENT && errno != EACCES)\n \t\t\tdie_errno(\"unable to open %s\", fn);\n-\t\treturn;\n+\t\treturn found_credential;\n \t}\n \n \twhile (strbuf_getline(&line, fh, '\\n') != EOF) {\n \t\tcredential_from_url(&entry, line.buf);\n \t\tif (entry.username && entry.password &&\n \t\t    credential_match(c, &entry)) {\n+\t\t\tfound_credential = 1;\n \t\t\tif (match_cb) {\n \t\t\t\tmatch_cb(&entry);\n \t\t\t\tbreak;\n@@ -38,6 +40,7 @@ static void parse_credential_file(const char *fn,\n \tcredential_clear(&entry);\n \tstrbuf_release(&line);\n \tfclose(fh);\n+\treturn found_credential;\n }\n \n static void print_entry(struct credential *c)\n@@ -64,21 +67,10 @@ static void rewrite_credential_file(const char *fn, struct credential *c,\n \t\tdie_errno(\"unable to commit credential store\");\n }\n \n-static void store_credential(const char *fn, struct credential *c)\n+static void store_credential_file(const char *fn, struct credential *c)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\t/*\n-\t * Sanity check that what we are storing is actually sensible.\n-\t * In particular, we can't make a URL without a protocol field.\n-\t * Without either a host or pathname (depending on the scheme),\n-\t * we have no primary key. And without a username and password,\n-\t * we are not actually storing a credential.\n-\t */\n-\tif (!c->protocol || !(c->host || c->path) ||\n-\t    !c->username || !c->password)\n-\t\treturn;\n-\n \tstrbuf_addf(&buf, \"%s://\", c->protocol);\n \tstrbuf_addstr_urlencode(&buf, c->username, 1);\n \tstrbuf_addch(&buf, ':');\n@@ -95,8 +87,34 @@ static void store_credential(const char *fn, struct credential *c)\n \tstrbuf_release(&buf);\n }\n \n-static void remove_credential(const char *fn, struct credential *c)\n+static void store_credential(const struct string_list *fns, struct credential *c,\n+\t\t\t     unsigned int default_index)\n {\n+\tstruct string_list_item *fn;\n+\n+\t/*\n+\t * Sanity check that what we are storing is actually sensible.\n+\t * In particular, we can't make a URL without a protocol field.\n+\t * Without either a host or pathname (depending on the scheme),\n+\t * we have no primary key. And without a username and password,\n+\t * we are not actually storing a credential.\n+\t */\n+\tif (!c->protocol || !(c->host || c->path) || !c->username || !c->password)\n+\t\treturn;\n+\n+\tfor_each_string_list_item(fn, fns)\n+\t\tif (!access(fn->string, F_OK)) {\n+\t\t\tstore_credential_file(fn->string, c);\n+\t\t\treturn;\n+\t\t}\n+\t/* Write credential to the filename at default_index, creating it */\n+\tstore_credential_file(fns->items[default_index].string, c);\n+}\n+\n+static void remove_credential(const struct string_list *fns, struct credential *c)\n+{\n+\tstruct string_list_item *fn;\n+\n \t/*\n \t * Sanity check that we actually have something to match\n \t * against. The input we get is a restrictive pattern,\n@@ -105,14 +123,20 @@ static void remove_credential(const char *fn, struct credential *c)\n \t * to empty input. So explicitly disallow it, and require that the\n \t * pattern have some actual content to match.\n \t */\n-\tif (c->protocol || c->host || c->path || c->username)\n-\t\trewrite_credential_file(fn, c, NULL);\n+\tif (!c->protocol && !c->host && !c->path && !c->username)\n+\t\treturn;\n+\tfor_each_string_list_item(fn, fns)\n+\t\tif (!access(fn->string, F_OK))\n+\t\t\trewrite_credential_file(fn->string, c, NULL);\n }\n \n-static int lookup_credential(const char *fn, struct credential *c)\n+static void lookup_credential(const struct string_list *fns, struct credential *c)\n {\n-\tparse_credential_file(fn, c, print_entry, NULL);\n-\treturn c->username && c->password;\n+\tstruct string_list_item *fn;\n+\n+\tfor_each_string_list_item(fn, fns)\n+\t\tif (parse_credential_file(fn->string, c, print_entry, NULL))\n+\t\t\treturn; /* Found credential */\n }\n \n int main(int argc, char **argv)\n@@ -123,6 +147,7 @@ int main(int argc, char **argv)\n \t};\n \tconst char *op;\n \tstruct credential c = CREDENTIAL_INIT;\n+\tstruct string_list fns = STRING_LIST_INIT_NODUP;\n \tchar *file = NULL;\n \tstruct option options[] = {\n \t\tOPT_STRING(0, \"file\", &file, \"path\",\n@@ -139,18 +164,20 @@ int main(int argc, char **argv)\n \n \tif (!file)\n \t\tfile = expand_user_path(\"~/.git-credentials\");\n-\tif (!file)\n+\tif (file)\n+\t\tstring_list_append_nodup(&fns, file);\n+\telse\n \t\tdie(\"unable to set up default path; use --file\");\n \n \tif (credential_read(&c, stdin) < 0)\n \t\tdie(\"unable to read credential\");\n \n \tif (!strcmp(op, \"get\"))\n-\t\tlookup_credential(file, &c);\n+\t\tlookup_credential(&fns, &c);\n \telse if (!strcmp(op, \"erase\"))\n-\t\tremove_credential(file, &c);\n+\t\tremove_credential(&fns, &c);\n \telse if (!strcmp(op, \"store\"))\n-\t\tstore_credential(file, &c);\n+\t\tstore_credential(&fns, &c, fns.nr - 1);\n \telse\n \t\t; /* Ignore unknown operation. */\n \n-- \n2.1.4\n"},{"id":"257274","messageId":"1425801532-8483-3-git-send-email-pyokagan@gmail.com","threadId":"38752","inReplyTo":"1425801532-8483-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v2 2/4] git-credential-store: support XDG_CONFIG_HOME","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-08T07:58:50Z","receivedAt":"2015-03-08T07:58:50Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Add $XDG_CONFIG_HOME/git/credentials to the default credential search\npath of git-credential-store. This allows git-credential-store to\nsupport user-specific configuration files in accordance with the XDG\nbase directory specification[1].\n\n[1] http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html\n\n~/.git-credentials has a higher precedence than\n$XDG_CONFIG_HOME/git/credentials when looking up credentials.  This\nmeans that if any duplicate matching credentials are found in the xdg\nfile (due to ~/.git-credentials being updated by old versions of git or\noutdated tools), they will not be used at all. This is to give the user\nsome leeway in switching to old versions of git while keeping the xdg\ndirectory. This is consistent with the behavior of git-config.\n\nHowever, the higher precedence of ~/.git-credentials means that as long\nas ~/.git-credentials exist, all credentials will be written to the\n~/.git-credentials file even if the user has an xdg file as having a\n~/.git-credentials file indicates that the user wants to preserve\nbackwards-compatibility. This is also consistent with the behavior of\ngit-config.\n\nSince the xdg file will not be used unless it actually exists, to\nprevent the situation where some credentials are present in the xdg file\nwhile some are present in the home file, users are recommended to not\ncreate the xdg file if they require compatibility with old versions of\ngit or outdated tools. Note, though, that \"erase\" can be used to\nexplicitly erase matching credentials from all files.\n\nHelped-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n credential-store.c | 15 ++++++++++-----\n 1 file changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/credential-store.c b/credential-store.c\nindex 3455d7a..7b22a3a 100644\n--- a/credential-store.c\n+++ b/credential-store.c\n@@ -162,11 +162,16 @@ int main(int argc, char **argv)\n \t\tusage_with_options(usage, options);\n \top = argv[0];\n \n-\tif (!file)\n-\t\tfile = expand_user_path(\"~/.git-credentials\");\n-\tif (file)\n+\tif (file) {\n \t\tstring_list_append_nodup(&fns, file);\n-\telse\n+\t} else {\n+\t\tif ((file = expand_user_path(\"~/.git-credentials\")))\n+\t\t\tstring_list_append_nodup(&fns, file);\n+\t\thome_config_paths(NULL, &file, \"credentials\");\n+\t\tif (file)\n+\t\t\tstring_list_append_nodup(&fns, file);\n+\t}\n+\tif (!fns.nr)\n \t\tdie(\"unable to set up default path; use --file\");\n \n \tif (credential_read(&c, stdin) < 0)\n@@ -177,7 +182,7 @@ int main(int argc, char **argv)\n \telse if (!strcmp(op, \"erase\"))\n \t\tremove_credential(&fns, &c);\n \telse if (!strcmp(op, \"store\"))\n-\t\tstore_credential(&fns, &c, fns.nr - 1);\n+\t\tstore_credential(&fns, &c, 0);\n \telse\n \t\t; /* Ignore unknown operation. */\n \n-- \n2.1.4\n"},{"id":"257273","messageId":"1425801532-8483-4-git-send-email-pyokagan@gmail.com","threadId":"38752","inReplyTo":"1425801532-8483-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v2 3/4] docs/git-credential-store: document XDG file and precedence","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-08T07:58:51Z","receivedAt":"2015-03-08T07:58:51Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"git-credential-store now supports an additional default credential file\nat $XDG_CONFIG_HOME/git/credentials. However, ~/.git-credentials takes\nprecedence over it for backwards compatibility. To make the precedence\nordering explicit, add a new section FILES that lists out the credential\nfile paths in their order of precedence, and explains how the ordering\naffects the lookup, storage and erase operations.\n\nAlso update documentation for --store to briefly explain the operations\non multiple files if the --store option is not provided.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Documentation/git-credential-store.txt | 37 ++++++++++++++++++++++++++++++++--\n 1 file changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt\nindex bc97071..451c4fa 100644\n--- a/Documentation/git-credential-store.txt\n+++ b/Documentation/git-credential-store.txt\n@@ -31,10 +31,43 @@ OPTIONS\n \n --file=<path>::\n \n-\tUse `<path>` to store credentials. The file will have its\n+\tUse `<path>` to lookup and store credentials. The file will have its\n \tfilesystem permissions set to prevent other users on the system\n \tfrom reading it, but will not be encrypted or otherwise\n-\tprotected. Defaults to `~/.git-credentials`.\n+\tprotected. If not specified, credentials will be searched for from\n+\t`~/.git-credentials` and `$XDG_CONFIG_HOME/git/credentials`, and\n+\tcredentials will be written to `~/.git-credentials` if it exists, or\n+\t`$XDG_CONFIG_HOME/git/credentials` if it exists and the former does\n+\tnot. See also <<FILES>>.\n+\n+[[FILES]]\n+FILES\n+-----\n+\n+If not set explicitly with '--file', there are two files where\n+git-credential-store will search for credentials in order of precedence:\n+\n+~/.git-credentials::\n+\tUser-specific credentials file.\n+\n+$XDG_CONFIG_HOME/git/credentials::\n+\tSecond user-specific credentials file. If '$XDG_CONFIG_HOME' is not set\n+\tor empty, `$HOME/.config/git/credentials` will be used. Any credentials\n+\tstored in this file will not be used if `~/.git-credentials` has a\n+\tmatching credential as well. It is a good idea not to create this file\n+\tif you sometimes use older versions of Git, as support for this file\n+\twas added fairly recently.\n+\n+\n+For credential lookups, the files are read in the order given above, with the\n+first matching credential found taking precedence over credentials found in\n+files further down the list.\n+\n+Credential storage will per default write to the first existing file in the\n+list. If none of these files exist, `~/.git-credentials` will be created and\n+written to.\n+\n+When erasing credentials, matching credentials will be erased from all files.\n \n EXAMPLES\n --------\n-- \n2.1.4\n"},{"id":"257275","messageId":"1425801532-8483-5-git-send-email-pyokagan@gmail.com","threadId":"38752","inReplyTo":"1425801532-8483-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v2 4/4] t0302: test credential-store support for XDG_CONFIG_HOME","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-08T07:58:52Z","receivedAt":"2015-03-08T07:58:52Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"t0302 now tests git-credential-store's support for the XDG user-specific\nconfiguration file $XDG_CONFIG_HOME/git/credentials. Specifically:\n\n* Ensure that the XDG file is strictly opt-in. It should not be created\n  by git at all times if it does not exist.\n\n* On the flip side, if the XDG file exists, ~/.git-credentials should\n  not be created at all times.\n\n* If both the XDG file and ~/.git-credentials exists, then both files\n  should be used for credential lookups. However, credentials should\n  only be written to ~/.git-credentials.\n\n* Credentials must be erased from both files.\n\n* $XDG_CONFIG_HOME can be a custom directory set by the user as per the\n  XDG base directory specification. Test that git-credential-store\n  respects that, but defaults to \"~/.config/git/credentials\" if it does\n  not exist or is empty.\n\nHelped-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n t/t0302-credential-store.sh | 92 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 92 insertions(+)\n\ndiff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh\nindex f61b40c..7fe832d 100755\n--- a/t/t0302-credential-store.sh\n+++ b/t/t0302-credential-store.sh\n@@ -5,5 +5,97 @@ test_description='credential-store tests'\n . \"$TEST_DIRECTORY\"/lib-credential.sh\n \n helper_test store\n+test_expect_success '~/.git-credentials is written to when xdg credentials file does not exist' '\n+\ttest -s \"$HOME/.git-credentials\"\n+'\n+test_expect_success 'xdg credentials file will not be created if it does not exist' '\n+\ttest_path_is_missing \"$HOME/.config/git/credentials\"\n+'\n+test_expect_success 'create $XDG_CONFIG_HOME/git/credentials file' '\n+\ttest_might_fail rm \"$HOME/.git-credentials\" &&\n+\tmkdir -p \"$HOME/.config/git\" &&\n+\t>\"$HOME/.config/git/credentials\"\n+'\n+helper_test store\n+test_expect_success 'xdg credentials file will be written to if it exists' '\n+\ttest -s \"$HOME/.config/git/credentials\"\n+'\n+test_expect_success '~/.git-credentials will not be created if xdg credentials file exists' '\n+\ttest_path_is_missing \"$HOME/.git-credentials\"\n+'\n+test_expect_success 'set up custom XDG_CONFIG_HOME credential file' '\n+\tXDG_CONFIG_HOME=\"$HOME/xdg\" && export XDG_CONFIG_HOME &&\n+\tmkdir -p \"$HOME/xdg/git\" &&\n+\t>\"$HOME/xdg/git/credentials\" &&\n+\t>\"$HOME/.config/git/credentials\"\n+'\n+helper_test store\n+test_expect_success 'custom XDG_CONFIG_HOME credentials file will be written to if it exists' '\n+\tunset XDG_CONFIG_HOME &&\n+\ttest -s \"$HOME/xdg/git/credentials\"\n+'\n+test_expect_success '~/.config/git/credentials file will not be written to if a custom XDG_CONFIG_HOME is set' '\n+\ttest_must_be_empty \"$HOME/.config/git/credentials\"\n+'\n+test_expect_success '~/.git-credentials will not be created if xdg credentials file exists' '\n+\ttest_path_is_missing \"$HOME/.git-credentials\"\n+'\n+test_expect_success 'get: return credentials from home file if matches are found in both home and xdg files' '\n+\tmkdir -p \"$HOME/.config/git\" &&\n+\techo \"https://xdg-user:xdg-pass@example.com\" >\"$HOME/.config/git/credentials\" &&\n+\techo \"https://home-user:home-pass@example.com\" >\"$HOME/.git-credentials\" &&\n+\tcheck fill store <<-\\EOF\n+\tprotocol=https\n+\thost=example.com\n+\t--\n+\tprotocol=https\n+\thost=example.com\n+\tusername=home-user\n+\tpassword=home-pass\n+\t--\n+\tEOF\n+'\n+test_expect_success 'get: return credentials from xdg file if the home files do not have them' '\n+\tmkdir -p \"$HOME/.config/git\" &&\n+\t>\"$HOME/.git-credentials\" &&\n+\techo \"https://home-user:home-pass@example.com\" >\"$HOME/.config/git/credentials\" &&\n+\tcheck fill store <<-\\EOF\n+\tprotocol=https\n+\thost=example.com\n+\t--\n+\tprotocol=https\n+\thost=example.com\n+\tusername=home-user\n+\tpassword=home-pass\n+\t--\n+\tEOF\n+'\n+test_expect_success 'get: return credentials from home file if xdg files are unreadable' '\n+\tmkdir -p \"$HOME/.config/git\" &&\n+\techo \"https://xdg-user:xdg-pass@example.com\" >\"$HOME/.config/git/credentials\" &&\n+\techo \"https://home-user:home-pass@example.com\" >\"$HOME/.git-credentials\" &&\n+\tchmod -r \"$HOME/.config/git/credentials\" &&\n+\tcheck fill store <<-\\EOF\n+\tprotocol=https\n+\thost=example.com\n+\t--\n+\tprotocol=https\n+\thost=example.com\n+\tusername=home-user\n+\tpassword=home-pass\n+\t--\n+\tEOF\n+'\n+test_expect_success 'erase: erase matching credentials from both xdg and home files' '\n+\tmkdir -p \"$HOME/.config/git\" &&\n+\techo \"https://xdg-user:xdg-pass@example.com\" >\"$HOME/.config/git/credentials\" &&\n+\techo \"https://home-user:home-pass@example.com\" >\"$HOME/.git-credentials\" &&\n+\tcheck reject store <<-\\EOF\n+\tprotocol=https\n+\thost=example.com\n+\tEOF\n+\ttest_must_be_empty \"$HOME/.config/git/credentials\" &&\n+\ttest_must_be_empty \"$HOME/.git-credentials\"\n+'\n \n test_done\n-- \n2.1.4\n"},{"id":"257395","messageId":"vpqk2yqcppi.fsf@anie.imag.fr","threadId":"38752","inReplyTo":"1425801532-8483-5-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH v2 4/4] t0302: test credential-store support for XDG_CONFIG_HOME","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-03-09T12:36:41Z","receivedAt":"2015-03-09T12:36:41Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Paul Tan <pyokagan@gmail.com> writes:\n\n> diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh\n> index f61b40c..7fe832d 100755\n> --- a/t/t0302-credential-store.sh\n> +++ b/t/t0302-credential-store.sh\n> @@ -5,5 +5,97 @@ test_description='credential-store tests'\n>  . \"$TEST_DIRECTORY\"/lib-credential.sh\n>  \n>  helper_test store\n> +test_expect_success '~/.git-credentials is written to when xdg credentials file does not exist' '\n> +\ttest -s \"$HOME/.git-credentials\"\n> +'\n> +test_expect_success 'xdg credentials file will not be created if it does not exist' '\n\nWe usually put a blank line between tests.\n\n> +helper_test store\n\nThat seems a bit \"brute force\" to me to re-run all tests several times.\nIt's probably OK since the tests are quite fast though.\n\n> +test_expect_success 'set up custom XDG_CONFIG_HOME credential file' '\n> +\tXDG_CONFIG_HOME=\"$HOME/xdg\" && export XDG_CONFIG_HOME &&\n> +\tmkdir -p \"$HOME/xdg/git\" &&\n\nI'd spell that \"$XDG_CONFIG_HOME\"/git instead, but that's not really\nimportant.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"257468","messageId":"CACRoPnRoY=c5NukCCS0OPMaL1TvqSuSL036-mK+4sae_6VgrVA@mail.gmail.com","threadId":"38752","inReplyTo":"1425801532-8483-3-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH v2 2/4] git-credential-store: support XDG_CONFIG_HOME","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-10T13:43:03Z","receivedAt":"2015-03-10T13:43:03Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Sun, Mar 8, 2015 at 3:58 PM, Paul Tan <pyokagan@gmail.com> wrote:\n>                 remove_credential(&fns, &c);\n>         else if (!strcmp(op, \"store\"))\n> -               store_credential(&fns, &c, fns.nr - 1);\n> +               store_credential(&fns, &c, 0);\n>         else\n>                 ; /* Ignore unknown operation. */\n\nWhoops, seems like I squashed some commits in the wrong order (but\nthere is no functional difference). Will re-roll the patch series.\n\nAlso, store_credential() should take a string as the default filename\ninstead of an index into the filename string_list for extra API\nflexibility.\n\nThanks.\n"},{"id":"257472","messageId":"CACRoPnRXH7ozCvdxLORKMAw=GwK65NzTd-e7qtvKiQHu-qAs=Q@mail.gmail.com","threadId":"38752","inReplyTo":"vpqk2yqcppi.fsf@anie.imag.fr","subject":"Re: [PATCH v2 4/4] t0302: test credential-store support for XDG_CONFIG_HOME","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-03-10T13:55:33Z","receivedAt":"2015-03-10T13:55:33Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Mon, Mar 9, 2015 at 8:36 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Paul Tan <pyokagan@gmail.com> writes:\n>> +'\n>> +test_expect_success 'xdg credentials file will not be created if it does not exist' '\n>\n> We usually put a blank line between tests.\n\nOkay, will do that.\n\n>> +helper_test store\n>\n> That seems a bit \"brute force\" to me to re-run all tests several times.\n> It's probably OK since the tests are quite fast though.\n\nThe credential helper tests are re-run because I believe that tests\nshould make no assumptions on the underlying implementation. e.g. the\npresence of the xdg file only may activate a code path that causes\ncredential storage to succeed but retrieval to fail.\n\n>> +test_expect_success 'set up custom XDG_CONFIG_HOME credential file' '\n>> +     XDG_CONFIG_HOME=\"$HOME/xdg\" && export XDG_CONFIG_HOME &&\n>> +     mkdir -p \"$HOME/xdg/git\" &&\n>\n> I'd spell that \"$XDG_CONFIG_HOME\"/git instead, but that's not really\n> important.\n\nOkay, will do that.\n\nThanks for the review.\n"}]}