{"thread":{"id":"27577","subject":"Git-Mediawiki : cloning a set of pages","startedAt":"2011-06-08T11:19:38Z","lastAt":"2011-06-13T19:22:00Z","messageCount":25,"participants":["Claire Fousse","Jeff King","Sverre Rabbelier","Jakub Narebski","Bert Wesarg","Matthieu Moy","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"169594","messageId":"BANLkTim1hOi0JdWZPR=Vw-S+9jTxqQ-=Tw@mail.gmail.com","threadId":"27577","inReplyTo":null,"subject":"Git-Mediawiki : cloning a set of pages","fromName":"Claire Fousse","fromEmail":"claire.fousse@ensimag.imag.fr","sentAt":"2011-06-08T11:19:38Z","receivedAt":"2011-06-08T11:19:38Z","isPatch":false,"sender":{"key":"claire.fousse@ensimag.imag.fr","avatar":null},"body":"Hi,\n\nI work on the Git-Mediawiki project.\n(http://lists-archives.org/git/746959-gate-between-git-and-mediawiki-remote-helpers.html).\n\nI'm trying to establish a feature to clone and work on some pages of a\nmediawiki instead of all of them.\n\nThe problem is not the feature in itself but the way you call it.\nJust so you remember, here is the command  to clone the mediawiki :\ngit clone mediawiki::http://yourwiki.com\n\nAs it is now, git clone does not implement a way to define a set of pages.\nThe 2 solutions we think of are :\n\t* git clone mediawiki::http://yourwiki.com$$page1$$page2 ...\n\tWhere $$ is a separator still to be determined. It should not be\nsomething which could appear in the title of a page.\n\tIt is a simple way to proceed but it becomes horrible when you want\nto clone many pages.\n\n\t* write a git-mw-clone script which asks the user to enter a set of\npages  and may store this set of\n\ttitles in the git config. This script should then call git-clone\nwhich will call the remote-mediawiki functions.\n\tgit-mw-clone would clone the entire wiki and git-mw-clone --pages\nwould ask the user to enter their set.\n\tThe problem here is that a not git-like command is required.\n\nWhat do you think about those solutions? Do you think of a better one?\n\nThank you very much.\n\n--\nClaire Fousse\nGrenoble INP - Ensimag\nclaire.fousse@ensimag.imag.fr\n"},{"id":"169620","messageId":"20110608151940.GD7805@sigill.intra.peff.net","threadId":"27577","inReplyTo":"BANLkTim1hOi0JdWZPR=Vw-S+9jTxqQ-=Tw@mail.gmail.com","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-08T15:19:40Z","receivedAt":"2011-06-08T15:19:40Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 08, 2011 at 01:19:38PM +0200, Claire Fousse wrote:\n\n> The problem is not the feature in itself but the way you call it.\n> Just so you remember, here is the command  to clone the mediawiki :\n> git clone mediawiki::http://yourwiki.com\n> \n> As it is now, git clone does not implement a way to define a set of pages.\n> The 2 solutions we think of are :\n> \t* git clone mediawiki::http://yourwiki.com$$page1$$page2 ...\n> \tWhere $$ is a separator still to be determined. It should not be\n> something which could appear in the title of a page.\n> \tIt is a simple way to proceed but it becomes horrible when you want\n> to clone many pages.\n\nIck, yeah, that is kind of ugly. I think this is a general problem with\nthe clone and remote helper interface that we are going to need to\nsolve. It seems like clone should allow transport-specific options to\npass through the command line and make it to the transport.\n\nSomething like:\n\n  git clone -c option=value mediawiki::http://...\n\nwhere the \"option\" is up to the transport to interpret.  It could be\nimplemented as a set of in-core options that get passed to the remote\nhelper over the pipe. But that leaves the helper with probably having to\nstore the options for future runs.\n\nMaybe it would be even simpler and more flexible to give clone a \"-c\"\nflag that writes specific config variables in the newly-created\nrepository. Like:\n\n  git clone -c mediawiki.page=page1 \\\n            -c mediawiki.page=page2 \\\n            http://...\n\nThen the remote helper can just consult the git config. As a bonus, it\nalso lets you do things like:\n\n  git clone -c core.ignorecase=true git://...\n\nwhich is currently awkward (you either have to have set such variable in\nyour ~/.gitconfig, or you must use init+config+fetch to do a clone\nmanually.\n\nGetting back to mediawiki, that gives us a slightly nicer syntax, but\nwe're still specifying each page on the command line (and now it's even\nmore verbose!). I would think two things could help:\n\n  1. Some kind of globbing, like mediawiki.page=\"foo_*\". The usefulness\n     of this will depend on how well pages in the wiki are named,\n     though.\n\n  2. Have a config option to point to a file containing page entries,\n     one per line.\n\n> \t* write a git-mw-clone script which asks the user to enter a set\n> \tof pages  and may store this set of titles in the git config.\n> \tThis script should then call git-clone which will call the\n> \tremote-mediawiki functions.  git-mw-clone would clone the entire\n> \twiki and git-mw-clone --pages would ask the user to enter their\n> \tset.  The problem here is that a not git-like command is\n> \trequired.\n\nYeah, I like this less because you lose a lot of the seamlessness of the\nremote helper solution.\n\n-Peff\n"},{"id":"169639","messageId":"BANLkTim54YGYN50gZtqwe8w6-du_aTDWTQ@mail.gmail.com","threadId":"27577","inReplyTo":"20110608151940.GD7805@sigill.intra.peff.net","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2011-06-08T17:04:27Z","receivedAt":"2011-06-08T17:04:27Z","isPatch":false,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Wed, Jun 8, 2011 at 17:19, Jeff King <peff@peff.net> wrote:\n> Maybe it would be even simpler and more flexible to give clone a \"-c\"\n> flag that writes specific config variables in the newly-created\n> repository.\n\nThis makes a lot of sense. What about a use case like git-svn supports\ncurrently, where you want to indicate \"use the default\ntrunk/branches/tags setup\"? Yes, you could do `git clone -c defaults\nsvn://example.com` and then git-remote-svn can set the relevant config\noptions itself. The only downside is that git-remote-svn then needs to\nunset 'defaults' and set the appropriate values itself (to avoid\ncluttering the config file). Thoughts?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"169643","messageId":"20110608171338.GA3917@sigill.intra.peff.net","threadId":"27577","inReplyTo":"BANLkTim54YGYN50gZtqwe8w6-du_aTDWTQ@mail.gmail.com","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-08T17:13:38Z","receivedAt":"2011-06-08T17:13:38Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 08, 2011 at 07:04:27PM +0200, Sverre Rabbelier wrote:\n\n> On Wed, Jun 8, 2011 at 17:19, Jeff King <peff@peff.net> wrote:\n> > Maybe it would be even simpler and more flexible to give clone a \"-c\"\n> > flag that writes specific config variables in the newly-created\n> > repository.\n> \n> This makes a lot of sense. What about a use case like git-svn supports\n> currently, where you want to indicate \"use the default\n> trunk/branches/tags setup\"? Yes, you could do `git clone -c defaults\n> svn://example.com` and then git-remote-svn can set the relevant config\n> options itself. The only downside is that git-remote-svn then needs to\n> unset 'defaults' and set the appropriate values itself (to avoid\n> cluttering the config file). Thoughts?\n\nYeah, I'm not sure. There is a similar case in the mediawiki helper\nitself, too. If I say:\n\n  git clone -c mediawiki.pagesfiles /path/to/pages mediawiki::...\n\nShould that add config that looks at /path/to/pages every time I fetch?\nShould it parse the pages file and load it into the config file as a set\nof config options? Should it copy the pages file into the .git directory\nand rewrite the config option to point to the local version?\n\nSo I think that while setting config options may be useful for some\nthings, the right model for other things is more like a command-line\noption. Maybe we need to offer both forms.\n\n-Peff\n"},{"id":"169644","messageId":"m3lixcdz67.fsf@localhost.localdomain","threadId":"27577","inReplyTo":"BANLkTim1hOi0JdWZPR=Vw-S+9jTxqQ-=Tw@mail.gmail.com","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-06-08T17:14:22Z","receivedAt":"2011-06-08T17:14:22Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Claire Fousse <claire.fousse@ensimag.imag.fr> writes:\n\n> The problem is not the feature in itself but the way you call it.\n> Just so you remember, here is the command  to clone the mediawiki :\n> git clone mediawiki::http://yourwiki.com\n> \n> As it is now, git clone does not implement a way to define a set of pages.\n[...]\n\nWell, what you need to do is to implement API for partial _clone_ (we\nhave some SPI for partial checkout, but that is slightly different\nbeast).\n\nCurrently we have --depth=<n> to limit depth of history when cloning,\nand \"git remote add -t <branch>\" (repeated if necessary) to consider\nonly a subset of branches, though unfortunately not in \"git clone\"\nyet.\n\nNot what you wanted to hear, I guess... :-(\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"169745","messageId":"BANLkTin3eEJfXfB7aadxue-QMduimvh-tg@mail.gmail.com","threadId":"27577","inReplyTo":"m3lixcdz67.fsf@localhost.localdomain","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Claire Fousse","fromEmail":"claire.fousse@ensimag.imag.fr","sentAt":"2011-06-09T09:06:16Z","receivedAt":"2011-06-09T09:06:16Z","isPatch":false,"sender":{"key":"claire.fousse@ensimag.imag.fr","avatar":null},"body":">> The problem is not the feature in itself but the way you call it.\n>> Just so you remember, here is the command  to clone the mediawiki :\n>> git clone mediawiki::http://yourwiki.com\n>>\n>> As it is now, git clone does not implement a way to define a set of pages.\n> [...]\n>\n> Well, what you need to do is to implement API for partial _clone_ (we\n> have some SPI for partial checkout, but that is slightly different\n> beast).\n>\n> Currently we have --depth=<n> to limit depth of history when cloning,\n> and \"git remote add -t <branch>\" (repeated if necessary) to consider\n> only a subset of branches, though unfortunately not in \"git clone\"\n> yet.\n>\n> Not what you wanted to hear, I guess... :-(\n\n\nYes, not really what I wanted to hear, but I had the feeling it would\ngo this way =).\n\nOur school project ends tomorrow, so starting tomorrow we will have less time\nto work on this.\n\nWe chose to implement the first solution with ##:\ngit clone mediawiki::http://yourwiki.com##page1##page2..\nThis way that feature can be used for now, and It will be really\nsimple to change the\ncommand later.\n\n-- \nClaire Fousse\nGrenoble INP - Ensimag\n2A Télécommunication\nclaire.fousse@ensimag.imag.fr\n"},{"id":"169770","messageId":"20110609155001.GA14969@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110608151940.GD7805@sigill.intra.peff.net","subject":"Re: Git-Mediawiki : cloning a set of pages","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:50:01Z","receivedAt":"2011-06-09T15:50:01Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 08, 2011 at 11:19:40AM -0400, Jeff King wrote:\n\n> Maybe it would be even simpler and more flexible to give clone a \"-c\"\n> flag that writes specific config variables in the newly-created\n> repository. Like:\n> \n>   git clone -c mediawiki.page=page1 \\\n>             -c mediawiki.page=page2 \\\n>             http://...\n> \n> Then the remote helper can just consult the git config. As a bonus, it\n> also lets you do things like:\n> \n>   git clone -c core.ignorecase=true git://...\n> \n> which is currently awkward (you either have to have set such variable in\n> your ~/.gitconfig, or you must use init+config+fetch to do a clone\n> manually.\n\nThis turned out to be a very tiny amount of code, but I found a ton\nof other bugs while working on it. :)\n\nSo the patch series is long, but the important bits are at the end. I\nfactored the code from the existing \"git -c\", so most of the bugfixes\nare there.\n\n  [01/10]: strbuf_split: add a max parameter\n  [02/10]: fix \"git -c\" parsing of values with equals signs\n\nThese two are the first bugfix.\n\n  [03/10]: config: die on error in command-line config\n\nAnother bugfix.\n\n  [04/10]: config: avoid segfault when parsing command-line config\n\nAnother bugfix.\n\n  [05/10]: strbuf: allow strbuf_split to work on non-strbufs\n  [06/10]: config: use strbuf_split_str instead of a temporary strbuf\n\nPlugging a memory leak.\n\n  [07/10]: parse-options: add OPT_STRING_LIST helper\n  [08/10]: remote: use new OPT_STRING_LIST\n  [09/10]: config: make git_config_parse_parameter a public function\n\nThese are refactoring for 10/10.\n\n  [10/10]: clone: accept config options on the command line\n\nAnd this is the actual patch.\n\n-Peff\n"},{"id":"169771","messageId":"20110609155121.GA25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 01/10] strbuf_split: add a max parameter","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:51:22Z","receivedAt":"2011-06-09T15:51:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Sometimes when splitting, you only want a limited number of\nfields, and for the final field to contain \"everything\nelse\", even if it includes the delimiter.\n\nThis patch introduces strbuf_split_max, which provides a\n\"max number of fields\" parameter; it behaves similarly to\nperl's \"split\" with a 3rd field.\n\nThe existing 2-argument form of strbuf_split is retained for\ncompatibility and ease-of-use.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nI am tempted to just call this new one strbuf_split and update all\ncallers. There aren't that many.\n\n strbuf.c |    7 +++++--\n strbuf.h |    7 ++++++-\n 2 files changed, 11 insertions(+), 3 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 09c43ae..64f6c1e 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -103,7 +103,7 @@ void strbuf_ltrim(struct strbuf *sb)\n \tsb->buf[sb->len] = '\\0';\n }\n \n-struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n+struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n {\n \tint alloc = 2, pos = 0;\n \tchar *n, *p;\n@@ -114,7 +114,10 @@ struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n \tp = n = sb->buf;\n \twhile (n < sb->buf + sb->len) {\n \t\tint len;\n-\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n+\t\tif (max <= 0 || pos + 1 < max)\n+\t\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n+\t\telse\n+\t\t\tn = NULL;\n \t\tif (pos + 1 >= alloc) {\n \t\t\talloc = alloc * 2;\n \t\t\tret = xrealloc(ret, sizeof(struct strbuf *) * alloc);\ndiff --git a/strbuf.h b/strbuf.h\nindex 9e6d9fa..4cf1dcd 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -44,7 +44,12 @@ extern void strbuf_rtrim(struct strbuf *);\n extern void strbuf_ltrim(struct strbuf *);\n extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);\n \n-extern struct strbuf **strbuf_split(const struct strbuf *, int delim);\n+extern struct strbuf **strbuf_split_max(const struct strbuf *,\n+\t\t\t\t\tint delim, int max);\n+static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n+{\n+\treturn strbuf_split_max(sb, delim, 0);\n+}\n extern void strbuf_list_free(struct strbuf **);\n \n /*----- add data in your buffer -----*/\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169772","messageId":"20110609155136.GB25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 02/10] fix \"git -c\" parsing of values with equals signs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:51:36Z","receivedAt":"2011-06-09T15:51:36Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"If you do something like:\n\n  git -c core.foo=\"value with = in it\" ...\n\nwe would split your option on \"=\" into three fields and\nthrow away the third one. With this patch we correctly take\neverything after the first \"=\" as the value (keys cannot\nhave an equals sign in them, so the parsing is unambiguous).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n config.c               |    2 +-\n t/t1300-repo-config.sh |    6 ++++++\n 2 files changed, 7 insertions(+), 1 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex e0b3b80..aa5eb78 100644\n--- a/config.c\n+++ b/config.c\n@@ -45,7 +45,7 @@ static int git_config_parse_parameter(const char *text,\n \tstruct strbuf tmp = STRBUF_INIT;\n \tstruct strbuf **pair;\n \tstrbuf_addstr(&tmp, text);\n-\tpair = strbuf_split(&tmp, '=');\n+\tpair = strbuf_split_max(&tmp, '=', 2);\n \tif (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')\n \t\tstrbuf_setlen(pair[0], pair[0]->len - 1);\n \tstrbuf_trim(pair[0]);\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 3db5626..ca5058e 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -904,4 +904,10 @@ test_expect_success 'git -c works with aliases of builtins' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git -c does not split values on equals' '\n+\techo \"value with = in it\" >expect &&\n+\tgit -c core.foo=\"value with = in it\" config core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169774","messageId":"20110609155232.GC25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 03/10] config: die on error in command-line config","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:52:32Z","receivedAt":"2011-06-09T15:52:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The error handling for git_config is somewhat confusing. We\ncollect errors from running git_config_from_file on the\nvarious config files and carefully pass them back up. But\nthe two odd things are:\n\n  1. We actually die on most errors in git_config_from_file.\n     In fact, the only error we actually pass back up is if\n     fopen() fails on the file.\n\n  2. Most callers of git_config do not check the error\n     return at all, but will continue if git_config reports\n     an error.\n\nWhen the code for \"git -c core.foo=bar\" was added, it\ndutifully passed errors up the call stack, only for them to\nbe eventually ignored. This makes it inconsistent with the\nfile-parsing code, which will die when it sees malformed\nconfig. And it's somewhat unsafe, because it means an error\nin parsing a typo like:\n\n  git -c clean.requireforce=ture clean\n\nwill continue the command, ignoring the config the user\ntried to give.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nAnother option would be to just make git_config call die() on error\ninstead of returning the error code that is ignored everywhere. But I\nwasn't sure if anybody was relying on the \"fopen failure is silently\nignored\" behavior.\n\n config.c               |    2 +-\n t/t1300-repo-config.sh |    8 ++++++++\n 2 files changed, 9 insertions(+), 1 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex aa5eb78..ebd404a 100644\n--- a/config.c\n+++ b/config.c\n@@ -856,7 +856,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)\n \n \tswitch (git_config_from_parameters(fn, data)) {\n \tcase -1: /* error */\n-\t\tret--;\n+\t\tdie(\"unable to parse command-line config\");\n \t\tbreak;\n \tcase 0: /* found nothing */\n \t\tbreak;\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex ca5058e..584e956 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -910,4 +910,12 @@ test_expect_success 'git -c does not split values on equals' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git -c dies on bogus config' '\n+\ttest_must_fail git -c core.bare=foo rev-parse\n+'\n+\n+test_expect_success 'git -c complains about empty key' '\n+\ttest_must_fail git -c \"=foo\" rev-parse\n+'\n+\n test_done\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169775","messageId":"20110609155243.GD25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 04/10] config: avoid segfault when parsing command-line config","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:52:43Z","receivedAt":"2011-06-09T15:52:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We already check for an empty key on the left side of an\nequals, but we would segfault if there was no content at\nall.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n config.c               |    2 ++\n t/t1300-repo-config.sh |    4 ++++\n 2 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex ebd404a..2517b62 100644\n--- a/config.c\n+++ b/config.c\n@@ -46,6 +46,8 @@ static int git_config_parse_parameter(const char *text,\n \tstruct strbuf **pair;\n \tstrbuf_addstr(&tmp, text);\n \tpair = strbuf_split_max(&tmp, '=', 2);\n+\tif (!pair[0])\n+\t\treturn error(\"bogus config parameter: %s\", text);\n \tif (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')\n \t\tstrbuf_setlen(pair[0], pair[0]->len - 1);\n \tstrbuf_trim(pair[0]);\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 584e956..3e140c1 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -918,4 +918,8 @@ test_expect_success 'git -c complains about empty key' '\n \ttest_must_fail git -c \"=foo\" rev-parse\n '\n \n+test_expect_success 'git -c complains about empty key and value' '\n+\ttest_must_fail git -c \"\" rev-parse\n+'\n+\n test_done\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169776","messageId":"20110609155458.GE25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 05/10] strbuf: allow strbuf_split to work on non-strbufs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:54:58Z","receivedAt":"2011-06-09T15:54:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The strbuf_split function takes a strbuf as input, and\noutputs a list of strbufs. However, there is no reason that\nthe input has to be a strbuf, and not an arbitrary buffer.\n\nThis patch adds strbuf_split_buf for a length-delimited\nbuffer, and strbuf_split_str for NUL-terminated strings.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis makes the naming convention slightly different than \"add\", which\nuses:\n\n  add - add data by pointer and length\n  addstr - add data by pointer to NUL-terminated string\n  addbuf - add strbuf\n\nbut here we have:\n\n  strbuf_split - split a strbuf\n  strbuf_split_str - split a NUL-terminated string\n  strbuf_split_buf - split pointer and length\n\nIt's that way because strbuf_split was introduced first with the current\nsemantics. I don't know if it is worth fixing the inconsistency.\n\n strbuf.c |   12 ++++++------\n strbuf.h |   12 +++++++++++-\n 2 files changed, 17 insertions(+), 7 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 64f6c1e..1a7df12 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -103,19 +103,19 @@ void strbuf_ltrim(struct strbuf *sb)\n \tsb->buf[sb->len] = '\\0';\n }\n \n-struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n+struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)\n {\n \tint alloc = 2, pos = 0;\n-\tchar *n, *p;\n+\tconst char *n, *p;\n \tstruct strbuf **ret;\n \tstruct strbuf *t;\n \n \tret = xcalloc(alloc, sizeof(struct strbuf *));\n-\tp = n = sb->buf;\n-\twhile (n < sb->buf + sb->len) {\n+\tp = n = str;\n+\twhile (n < str + slen) {\n \t\tint len;\n \t\tif (max <= 0 || pos + 1 < max)\n-\t\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n+\t\t\tn = memchr(n, delim, slen - (n - str));\n \t\telse\n \t\t\tn = NULL;\n \t\tif (pos + 1 >= alloc) {\n@@ -123,7 +123,7 @@ struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n \t\t\tret = xrealloc(ret, sizeof(struct strbuf *) * alloc);\n \t\t}\n \t\tif (!n)\n-\t\t\tn = sb->buf + sb->len - 1;\n+\t\t\tn = str + slen - 1;\n \t\tlen = n - p + 1;\n \t\tt = xmalloc(sizeof(struct strbuf));\n \t\tstrbuf_init(t, len);\ndiff --git a/strbuf.h b/strbuf.h\nindex 4cf1dcd..46a33f8 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -44,8 +44,18 @@ extern void strbuf_rtrim(struct strbuf *);\n extern void strbuf_ltrim(struct strbuf *);\n extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);\n \n-extern struct strbuf **strbuf_split_max(const struct strbuf *,\n+extern struct strbuf **strbuf_split_buf(const char *, size_t,\n \t\t\t\t\tint delim, int max);\n+static inline struct strbuf **strbuf_split_str(const char *str,\n+\t\t\t\t\t       int delim, int max)\n+{\n+\treturn strbuf_split_buf(str, strlen(str), delim, max);\n+}\n+static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,\n+\t\t\t\t\t\tint delim, int max)\n+{\n+\treturn strbuf_split_buf(sb->buf, sb->len, delim, max);\n+}\n static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n {\n \treturn strbuf_split_max(sb, delim, 0);\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169777","messageId":"20110609155509.GF25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 06/10] config: use strbuf_split_str instead of a temporary strbuf","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:55:09Z","receivedAt":"2011-06-09T15:55:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This saves an allocation and copy, and also fixes a minor\nmemory leak.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n config.c |    4 +---\n 1 files changed, 1 insertions(+), 3 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 2517b62..189b766 100644\n--- a/config.c\n+++ b/config.c\n@@ -42,10 +42,8 @@ void git_config_push_parameter(const char *text)\n static int git_config_parse_parameter(const char *text,\n \t\t\t\t      config_fn_t fn, void *data)\n {\n-\tstruct strbuf tmp = STRBUF_INIT;\n \tstruct strbuf **pair;\n-\tstrbuf_addstr(&tmp, text);\n-\tpair = strbuf_split_max(&tmp, '=', 2);\n+\tpair = strbuf_split_str(text, '=', 2);\n \tif (!pair[0])\n \t\treturn error(\"bogus config parameter: %s\", text);\n \tif (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169778","messageId":"20110609155523.GG25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 07/10] parse-options: add OPT_STRING_LIST helper","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:55:23Z","receivedAt":"2011-06-09T15:55:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This just adds repeated invocations of an option to a list\nof strings. Using the \"--no-<var>\" form will reset the list\nto empty.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n parse-options.c          |   17 +++++++++++++++++\n parse-options.h          |    4 ++++\n t/t0040-parse-options.sh |   17 +++++++++++++++++\n test-parse-options.c     |    6 ++++++\n 4 files changed, 44 insertions(+), 0 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex 73bd28a..879ea82 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -3,6 +3,7 @@\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"color.h\"\n+#include \"string-list.h\"\n \n static int parse_options_usage(struct parse_opt_ctx_t *ctx,\n \t\t\t       const char * const *usagestr,\n@@ -687,3 +688,19 @@ int parse_options_concat(struct option *dst, size_t dst_size, struct option *src\n \t}\n \treturn -1;\n }\n+\n+int parse_opt_string_list(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct string_list *v = opt->value;\n+\n+\tif (unset) {\n+\t\tstring_list_clear(v, 0);\n+\t\treturn 0;\n+\t}\n+\n+\tif (!arg)\n+\t\treturn -1;\n+\n+\tstring_list_append(v, xstrdup(arg));\n+\treturn 0;\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex d1b12fe..05eb09b 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -130,6 +130,9 @@ struct option {\n \t\t\t\t      (h), PARSE_OPT_NOARG, NULL, (p) }\n #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), \"n\", (h) }\n #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }\n+#define OPT_STRING_LIST(s, l, v, a, h) \\\n+\t\t\t\t    { OPTION_CALLBACK, (s), (l), (v), (a), \\\n+\t\t\t\t      (h), 0, &parse_opt_string_list }\n #define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \\\n \t\t\t\t      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }\n #define OPT_DATE(s, l, v, h) \\\n@@ -204,6 +207,7 @@ extern int parse_opt_color_flag_cb(const struct option *, const char *, int);\n extern int parse_opt_verbosity_cb(const struct option *, const char *, int);\n extern int parse_opt_with_commit(const struct option *, const char *, int);\n extern int parse_opt_tertiary(const struct option *, const char *, int);\n+extern int parse_opt_string_list(const struct option *, const char *, int);\n \n #define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', \"verbose\", (var), (h))\n #define OPT__QUIET(var, h)    OPT_BOOLEAN('q', \"quiet\",   (var), (h))\ndiff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh\nindex ae26614..007f39d 100755\n--- a/t/t0040-parse-options.sh\n+++ b/t/t0040-parse-options.sh\n@@ -28,6 +28,7 @@ String options\n     --st <st>             get another string (pervert ordering)\n     -o <str>              get another string\n     --default-string      set string to default\n+    --list <str>          add str to list\n \n Magic arguments\n     --quux                means --quux\n@@ -337,4 +338,20 @@ test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '\n \ttest_cmp expect output\n '\n \n+cat >>expect <<'EOF'\n+list: foo\n+list: bar\n+list: baz\n+EOF\n+test_expect_success '--list keeps list of strings' '\n+\ttest-parse-options --list foo --list=bar --list=baz >output &&\n+\ttest_cmp expect output\n+'\n+\n+test_expect_success '--no-list resets list' '\n+\ttest-parse-options --list=other --list=irrelevant --list=options \\\n+\t\t--no-list --list=foo --list=bar --list=baz >output &&\n+\ttest_cmp expect output\n+'\n+\n test_done\ndiff --git a/test-parse-options.c b/test-parse-options.c\nindex 4e3710b..91a5701 100644\n--- a/test-parse-options.c\n+++ b/test-parse-options.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"parse-options.h\"\n+#include \"string-list.h\"\n \n static int boolean = 0;\n static int integer = 0;\n@@ -9,6 +10,7 @@ static int verbose = 0, dry_run = 0, quiet = 0;\n static char *string = NULL;\n static char *file = NULL;\n static int ambiguous;\n+static struct string_list list;\n \n static int length_callback(const struct option *opt, const char *arg, int unset)\n {\n@@ -54,6 +56,7 @@ int main(int argc, const char **argv)\n \t\tOPT_STRING('o', NULL, &string, \"str\", \"get another string\"),\n \t\tOPT_SET_PTR(0, \"default-string\", &string,\n \t\t\t\"set string to default\", (unsigned long)\"default\"),\n+\t\tOPT_STRING_LIST(0, \"list\", &list, \"str\", \"add str to list\"),\n \t\tOPT_GROUP(\"Magic arguments\"),\n \t\tOPT_ARGUMENT(\"quux\", \"means --quux\"),\n \t\tOPT_NUMBER_CALLBACK(&integer, \"set integer to NUM\",\n@@ -85,6 +88,9 @@ int main(int argc, const char **argv)\n \tprintf(\"dry run: %s\\n\", dry_run ? \"yes\" : \"no\");\n \tprintf(\"file: %s\\n\", file ? file : \"(not set)\");\n \n+\tfor (i = 0; i < list.nr; i++)\n+\t\tprintf(\"list: %s\\n\", list.items[i].string);\n+\n \tfor (i = 0; i < argc; i++)\n \t\tprintf(\"arg %02d: %s\\n\", i, argv[i]);\n \n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169779","messageId":"20110609155558.GH25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 08/10] remote: use new OPT_STRING_LIST","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:55:59Z","receivedAt":"2011-06-09T15:55:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This saves us having our own callback function.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis isn't technically related to the rest of the series, but is a\ncleanup we can do because of the previous commit.\n\n builtin/remote.c |   14 ++------------\n 1 files changed, 2 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 9ff1cac..05b1f5b 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -88,16 +88,6 @@ static inline int postfixcmp(const char *string, const char *postfix)\n \treturn strcmp(string + len1 - len2, postfix);\n }\n \n-static int opt_parse_track(const struct option *opt, const char *arg, int not)\n-{\n-\tstruct string_list *list = opt->value;\n-\tif (not)\n-\t\tstring_list_clear(list, 0);\n-\telse\n-\t\tstring_list_append(list, arg);\n-\treturn 0;\n-}\n-\n static int fetch_remote(const char *name)\n {\n \tconst char *argv[] = { \"fetch\", name, NULL, NULL };\n@@ -176,8 +166,8 @@ static int add(int argc, const char **argv)\n \t\t\t    TAGS_SET),\n \t\tOPT_SET_INT(0, NULL, &fetch_tags,\n \t\t\t    \"or do not fetch any tag at all (--no-tags)\", TAGS_UNSET),\n-\t\tOPT_CALLBACK('t', \"track\", &track, \"branch\",\n-\t\t\t\"branch(es) to track\", opt_parse_track),\n+\t\tOPT_STRING_LIST('t', \"track\", &track, \"branch\",\n+\t\t\t\t\"branch(es) to track\"),\n \t\tOPT_STRING('m', \"master\", &master, \"branch\", \"master branch\"),\n \t\t{ OPTION_CALLBACK, 0, \"mirror\", &mirror, \"push|fetch\",\n \t\t\t\"set up remote as a mirror to push to or fetch from\",\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169780","messageId":"20110609155642.GI25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 09/10] config: make git_config_parse_parameter a public function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:56:42Z","receivedAt":"2011-06-09T15:56:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We use this internally to parse \"git -c core.foo=bar\", but\nthe general format of \"key=value\" is useful for other\nplaces.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis could perhaps have a better name than \"parameter\". I dunno.\n\n cache.h  |    2 ++\n config.c |    4 ++--\n 2 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex e11cf6a..933be72 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1063,6 +1063,8 @@ extern int config_error_nonbool(const char *);\n extern const char *get_log_output_encoding(void);\n extern const char *get_commit_output_encoding(void);\n \n+extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);\n+\n extern const char *config_exclusive_filename;\n \n #define MAX_GITNAME (1000)\ndiff --git a/config.c b/config.c\nindex 189b766..25d998c 100644\n--- a/config.c\n+++ b/config.c\n@@ -39,8 +39,8 @@ void git_config_push_parameter(const char *text)\n \tstrbuf_release(&env);\n }\n \n-static int git_config_parse_parameter(const char *text,\n-\t\t\t\t      config_fn_t fn, void *data)\n+int git_config_parse_parameter(const char *text,\n+\t\t\t       config_fn_t fn, void *data)\n {\n \tstruct strbuf **pair;\n \tpair = strbuf_split_str(text, '=', 2);\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169782","messageId":"20110609155740.GJ25507@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609155001.GA14969@sigill.intra.peff.net","subject":"[PATCH 10/10] clone: accept config options on the command line","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T15:57:40Z","receivedAt":"2011-06-09T15:57:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Clone does all of init, \"remote add\", fetch, and checkout\nwithout giving the user a chance to intervene and set any\nconfiguration. This patch allows you to set config options\nin the newly created repository after the clone, but before\nwe do any other operations.\n\nIn many cases, this is a minor convenience over something\nlike:\n\n  git clone git://...\n  git config core.whatever true\n\nBut in some cases, it can bring extra efficiency by changing\nhow the fetch or checkout work. For example, setting\nline-ending config before the checkout avoids having to\nre-checkout all of the contents with the correct line\nendings.\n\nIt also provides a mechanism for passing information to remote\nhelpers during a clone; the helpers may read the git config\nto influence how they operate.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nYay, the actual patch. Even if we don't end up using this for transport\nhelpers, I think it's a sane thing to allow (e.g., see the final test\nbelow).\n\n Documentation/git-clone.txt |    9 +++++++++\n builtin/clone.c             |   21 ++++++++++++++++++++-\n t/t5707-clone-config.sh     |   40 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 69 insertions(+), 1 deletions(-)\n create mode 100755 t/t5707-clone-config.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex b093e45..3e24064 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -159,6 +159,15 @@ objects from the source repository into a pack in the cloned repository.\n \tSpecify the directory from which templates will be used;\n \t(See the \"TEMPLATE DIRECTORY\" section of linkgit:git-init[1].)\n \n+--config <key>=<value>::\n+-c <key>=<value>::\n+\tSet a configuration variable in the newly-cloned repository. The\n+\tkey is in the same as expected by linkgit:git-config[1] (e.g.,\n+\t`core.eol=true`). If multiple values are given for the same key,\n+\teach value will be written to the config file. This makes it\n+\tsafe, for example, to add additional fetch refspecs to the\n+\torigin remote.\n+\n --depth <depth>::\n \tCreate a 'shallow' clone with a history truncated to the\n \tspecified number of revisions.  A shallow repository has a\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex f579794..a15784a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -46,6 +46,7 @@ static const char *real_git_dir;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n static int option_progress;\n+static struct string_list option_config;\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -83,7 +84,8 @@ static struct option builtin_clone_options[] = {\n \t\t    \"create a shallow clone of that depth\"),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, \"gitdir\",\n \t\t   \"separate git dir from working tree\"),\n-\n+\tOPT_STRING_LIST('c', \"config\", &option_config, \"key=value\",\n+\t\t\t\"set config inside the new repository\"),\n \tOPT_END()\n };\n \n@@ -364,6 +366,22 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static int write_one_config(const char *key, const char *value, void *data)\n+{\n+\treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n+}\n+\n+static void write_config(struct string_list *config)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < config->nr; i++) {\n+\t\tif (git_config_parse_parameter(config->items[i].string,\n+\t\t\t\t\t       write_one_config, NULL) < 0)\n+\t\t\tdie(\"unable to write parameters to config file\");\n+\t}\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -482,6 +500,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tprintf(_(\"Cloning into %s...\\n\"), dir);\n \t}\n \tinit_db(option_template, INIT_DB_QUIET);\n+\twrite_config(&option_config);\n \n \t/*\n \t * At this point, the config exists, so we do not need the\ndiff --git a/t/t5707-clone-config.sh b/t/t5707-clone-config.sh\nnew file mode 100755\nindex 0000000..27d730c\n--- /dev/null\n+++ b/t/t5707-clone-config.sh\n@@ -0,0 +1,40 @@\n+#!/bin/sh\n+\n+test_description='tests for git clone -c key=value'\n+. ./test-lib.sh\n+\n+test_expect_success 'clone -c sets config in cloned repo' '\n+\trm -rf child &&\n+\tgit clone -c core.foo=bar . child &&\n+\techo bar >expect &&\n+\tgit --git-dir=child/.git config core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c can set multi-keys' '\n+\trm -rf child &&\n+\tgit clone -c core.foo=bar -c core.foo=baz . child &&\n+\t{ echo bar; echo baz; } >expect &&\n+\tgit --git-dir=child/.git config --get-all core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c without a value is boolean true' '\n+\trm -rf child &&\n+\tgit clone -c core.foo . child &&\n+\techo true >expect &&\n+\tgit --git-dir=child/.git config --bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c config is available during clone' '\n+\techo content >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\trm -rf child &&\n+\tgit clone -c core.autocrlf . child &&\n+\tprintf \"content\\\\r\\\\n\" >expect &&\n+\ttest_cmp expect child/file\n+'\n+\n+test_done\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169790","messageId":"BANLkTinBqSVpREg6667Ydf2JjL3r14BgRA@mail.gmail.com","threadId":"27577","inReplyTo":"20110609155740.GJ25507@sigill.intra.peff.net","subject":"Re: [PATCH 10/10] clone: accept config options on the command line","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2011-06-09T17:10:33Z","receivedAt":"2011-06-09T17:10:33Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Thu, Jun 9, 2011 at 17:57, Jeff King <peff@peff.net> wrote:\n> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> index b093e45..3e24064 100644\n> --- a/Documentation/git-clone.txt\n> +++ b/Documentation/git-clone.txt\n> @@ -159,6 +159,15 @@ objects from the source repository into a pack in the cloned repository.\n>        Specify the directory from which templates will be used;\n>        (See the \"TEMPLATE DIRECTORY\" section of linkgit:git-init[1].)\n>\n> +--config <key>=<value>::\n> +-c <key>=<value>::\n> +       Set a configuration variable in the newly-cloned repository. The\n\nShouldn't this be 'newly-created repository', because the\nconfiguration variables are set before the actual cloning, but after\ninitializing the new repository.\n\nBert\n\n> +       key is in the same as expected by linkgit:git-config[1] (e.g.,\n> +       `core.eol=true`). If multiple values are given for the same key,\n> +       each value will be written to the config file. This makes it\n> +       safe, for example, to add additional fetch refspecs to the\n> +       origin remote.\n> +\n>  --depth <depth>::\n>        Create a 'shallow' clone with a history truncated to the\n>        specified number of revisions.  A shallow repository has a\n"},{"id":"169791","messageId":"20110609171214.GA30888@sigill.intra.peff.net","threadId":"27577","inReplyTo":"BANLkTinBqSVpREg6667Ydf2JjL3r14BgRA@mail.gmail.com","subject":"Re: [PATCH 10/10] clone: accept config options on the command line","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T17:12:14Z","receivedAt":"2011-06-09T17:12:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 09, 2011 at 07:10:33PM +0200, Bert Wesarg wrote:\n\n> On Thu, Jun 9, 2011 at 17:57, Jeff King <peff@peff.net> wrote:\n> > diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> > index b093e45..3e24064 100644\n> > --- a/Documentation/git-clone.txt\n> > +++ b/Documentation/git-clone.txt\n> > @@ -159,6 +159,15 @@ objects from the source repository into a pack in the cloned repository.\n> >        Specify the directory from which templates will be used;\n> >        (See the \"TEMPLATE DIRECTORY\" section of linkgit:git-init[1].)\n> >\n> > +--config <key>=<value>::\n> > +-c <key>=<value>::\n> > +       Set a configuration variable in the newly-cloned repository. The\n> \n> Shouldn't this be 'newly-created repository', because the\n> configuration variables are set before the actual cloning, but after\n> initializing the new repository.\n\nYeah, I think your wording is better. It might also make sense to\nexplicitly say that the config takes effect during the fetch and\ncheckout phases of the clone (there's otherwise no point, of course, but\nit never hurts to make the documentation clear).\n\n-Peff\n"},{"id":"169813","messageId":"20110609205619.GC4671@sigill.intra.peff.net","threadId":"27577","inReplyTo":"20110609171214.GA30888@sigill.intra.peff.net","subject":"Re: [PATCH 10/10] clone: accept config options on the command line","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-09T20:56:19Z","receivedAt":"2011-06-09T20:56:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 09, 2011 at 01:12:14PM -0400, Jeff King wrote:\n\n> > > +-c <key>=<value>::\n> > > +       Set a configuration variable in the newly-cloned repository. The\n> > \n> > Shouldn't this be 'newly-created repository', because the\n> > configuration variables are set before the actual cloning, but after\n> > initializing the new repository.\n> \n> Yeah, I think your wording is better. It might also make sense to\n> explicitly say that the config takes effect during the fetch and\n> checkout phases of the clone (there's otherwise no point, of course, but\n> it never hurts to make the documentation clear).\n\nThere was also an unrelated typo in the newly-added documentation. So\nhere's a replacement 10/10 with the fixed documentation.\n\n-- >8 --\nSubject: [PATCH] clone: accept config options on the command line\n\nClone does all of init, \"remote add\", fetch, and checkout\nwithout giving the user a chance to intervene and set any\nconfiguration. This patch allows you to set config options\nin the newly created repository after the clone, but before\nwe do any other operations.\n\nIn many cases, this is a minor convenience over something\nlike:\n\n  git clone git://...\n  git config core.whatever true\n\nBut in some cases, it can bring extra efficiency by changing\nhow the fetch or checkout work. For example, setting\nline-ending config before the checkout avoids having to\nre-checkout all of the contents with the correct line\nendings.\n\nIt also provides a mechanism for passing information to remote\nhelpers during a clone; the helpers may read the git config\nto influence how they operate.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-clone.txt |   11 +++++++++++\n builtin/clone.c             |   21 ++++++++++++++++++++-\n t/t5707-clone-config.sh     |   40 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 71 insertions(+), 1 deletions(-)\n create mode 100755 t/t5707-clone-config.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex b093e45..4b8b26b 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -159,6 +159,17 @@ objects from the source repository into a pack in the cloned repository.\n \tSpecify the directory from which templates will be used;\n \t(See the \"TEMPLATE DIRECTORY\" section of linkgit:git-init[1].)\n \n+--config <key>=<value>::\n+-c <key>=<value>::\n+\tSet a configuration variable in the newly-created repository;\n+\tthis takes effect immediately after the repository is\n+\tinitialized, but before the remote history is fetched or any\n+\tfiles checked out.  The key is in the same format as expected by\n+\tlinkgit:git-config[1] (e.g., `core.eol=true`). If multiple\n+\tvalues are given for the same key, each value will be written to\n+\tthe config file. This makes it safe, for example, to add\n+\tadditional fetch refspecs to the origin remote.\n+\n --depth <depth>::\n \tCreate a 'shallow' clone with a history truncated to the\n \tspecified number of revisions.  A shallow repository has a\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex f579794..a15784a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -46,6 +46,7 @@ static const char *real_git_dir;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n static int option_progress;\n+static struct string_list option_config;\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -83,7 +84,8 @@ static struct option builtin_clone_options[] = {\n \t\t    \"create a shallow clone of that depth\"),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, \"gitdir\",\n \t\t   \"separate git dir from working tree\"),\n-\n+\tOPT_STRING_LIST('c', \"config\", &option_config, \"key=value\",\n+\t\t\t\"set config inside the new repository\"),\n \tOPT_END()\n };\n \n@@ -364,6 +366,22 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static int write_one_config(const char *key, const char *value, void *data)\n+{\n+\treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n+}\n+\n+static void write_config(struct string_list *config)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < config->nr; i++) {\n+\t\tif (git_config_parse_parameter(config->items[i].string,\n+\t\t\t\t\t       write_one_config, NULL) < 0)\n+\t\t\tdie(\"unable to write parameters to config file\");\n+\t}\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -482,6 +500,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tprintf(_(\"Cloning into %s...\\n\"), dir);\n \t}\n \tinit_db(option_template, INIT_DB_QUIET);\n+\twrite_config(&option_config);\n \n \t/*\n \t * At this point, the config exists, so we do not need the\ndiff --git a/t/t5707-clone-config.sh b/t/t5707-clone-config.sh\nnew file mode 100755\nindex 0000000..27d730c\n--- /dev/null\n+++ b/t/t5707-clone-config.sh\n@@ -0,0 +1,40 @@\n+#!/bin/sh\n+\n+test_description='tests for git clone -c key=value'\n+. ./test-lib.sh\n+\n+test_expect_success 'clone -c sets config in cloned repo' '\n+\trm -rf child &&\n+\tgit clone -c core.foo=bar . child &&\n+\techo bar >expect &&\n+\tgit --git-dir=child/.git config core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c can set multi-keys' '\n+\trm -rf child &&\n+\tgit clone -c core.foo=bar -c core.foo=baz . child &&\n+\t{ echo bar; echo baz; } >expect &&\n+\tgit --git-dir=child/.git config --get-all core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c without a value is boolean true' '\n+\trm -rf child &&\n+\tgit clone -c core.foo . child &&\n+\techo true >expect &&\n+\tgit --git-dir=child/.git config --bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'clone -c config is available during clone' '\n+\techo content >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\trm -rf child &&\n+\tgit clone -c core.autocrlf . child &&\n+\tprintf \"content\\\\r\\\\n\" >expect &&\n+\ttest_cmp expect child/file\n+'\n+\n+test_done\n-- \n1.7.6.rc1.36.g91167\n"},{"id":"169820","messageId":"vpqtyby8wip.fsf@bauges.imag.fr","threadId":"27577","inReplyTo":"20110609155740.GJ25507@sigill.intra.peff.net","subject":"Re: [PATCH 10/10] clone: accept config options on the command line","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2011-06-09T22:34:06Z","receivedAt":"2011-06-09T22:34:06Z","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> Yay, the actual patch. Even if we don't end up using this for transport\n> helpers, I think it's a sane thing to allow (e.g., see the final test\n> below).\n\nAck.\n\nI reviewed quickly the serie, which seems OK. Nice work, amazing serie\nof patches to end up with this trivial one ;-).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"169956","messageId":"7voc21od0g.fsf@alter.siamese.dyndns.org","threadId":"27577","inReplyTo":"20110609155121.GA25507@sigill.intra.peff.net","subject":"Re: [PATCH 01/10] strbuf_split: add a max parameter","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-13T17:30:07Z","receivedAt":"2011-06-13T17:30:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I am tempted to just call this new one strbuf_split and update all\n> callers. There aren't that many.\n\nYes, that is indeed tempting, and because we have a new parameter the\ncompiler will catch any new callers that pop up in a mismerge so that\nwould be perfectly safe.\n\n> -struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n> +struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n>  {\n>  \tint alloc = 2, pos = 0;\n>  \tchar *n, *p;\n> @@ -114,7 +114,10 @@ struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n>  \tp = n = sb->buf;\n>  \twhile (n < sb->buf + sb->len) {\n>  \t\tint len;\n> -\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n> +\t\tif (max <= 0 || pos + 1 < max)\n> +\t\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n> +\t\telse\n> +\t\t\tn = NULL;\n>  \t\tif (pos + 1 >= alloc) {\n>  \t\t\talloc = alloc * 2;\n>  \t\t\tret = xrealloc(ret, sizeof(struct strbuf *) * alloc);\n\nHmm, even when we know the value of max, we go exponential, and even do so\nby hand without using ALLOC_GROW(). Somewhat sad.\n\nAlso do we currently rely on the bug that strbuf_split() returns (NULL,)\ninstead of (\"\", NULL) when given an empty string?  If not, perhaps...\n\n strbuf.c |   50 +++++++++++++++++++++++++++++++-------------------\n 1 files changed, 31 insertions(+), 19 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 09c43ae..7a8ee3a 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -103,31 +103,43 @@ void strbuf_ltrim(struct strbuf *sb)\n \tsb->buf[sb->len] = '\\0';\n }\n \n-struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n+struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n {\n-\tint alloc = 2, pos = 0;\n \tchar *n, *p;\n-\tstruct strbuf **ret;\n+\tstruct strbuf **ret = NULL;\n \tstruct strbuf *t;\n-\n-\tret = xcalloc(alloc, sizeof(struct strbuf *));\n-\tp = n = sb->buf;\n-\twhile (n < sb->buf + sb->len) {\n-\t\tint len;\n-\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n-\t\tif (pos + 1 >= alloc) {\n-\t\t\talloc = alloc * 2;\n-\t\t\tret = xrealloc(ret, sizeof(struct strbuf *) * alloc);\n+\tint pass, count = 0;\n+\n+\tfor (pass = 0; pass < 2; pass++) {\n+\t\t/* First pass counts, second pass allocates and fills */\n+\t\tif (pass)\n+\t\t\tret = xcalloc(count + 1 + !count, sizeof(struct strbuf *));\n+\t\tcount = 0;\n+\t\tn = sb->buf;\n+\t\twhile (n < sb->buf + sb->len) {\n+\t\t\tp = n;\n+\t\t\tif (max <= 0 || count + 1 < max)\n+\t\t\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n+\t\t\telse\n+\t\t\t\tn = NULL;\n+\t\t\tif (!n)\n+\t\t\t\tn = sb->buf + sb->len - 1;\n+\n+\t\t\tif (pass) {\n+\t\t\t\tint len = n - p + 1;\n+\t\t\t\tt = xmalloc(sizeof(struct strbuf));\n+\t\t\t\tstrbuf_init(t, len);\n+\t\t\t\tstrbuf_add(t, p, len);\n+\t\t\t\tret[count] = t;\n+\t\t\t}\n+\t\t\tcount++;\n+\t\t\tn++;\n \t\t}\n-\t\tif (!n)\n-\t\t\tn = sb->buf + sb->len - 1;\n-\t\tlen = n - p + 1;\n+\t}\n+\tif (!count) {\n \t\tt = xmalloc(sizeof(struct strbuf));\n-\t\tstrbuf_init(t, len);\n-\t\tstrbuf_add(t, p, len);\n-\t\tret[pos] = t;\n-\t\tret[++pos] = NULL;\n-\t\tp = ++n;\n+\t\tstrbuf_init(t, 0);\n+\t\tret[0] = t;\n \t}\n \treturn ret;\n }\n"},{"id":"169958","messageId":"7vhb7tod01.fsf@alter.siamese.dyndns.org","threadId":"27577","inReplyTo":"20110609155243.GD25507@sigill.intra.peff.net","subject":"Re: [PATCH 04/10] config: avoid segfault when parsing command-line config","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-13T17:30:22Z","receivedAt":"2011-06-13T17:30:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> We already check for an empty key on the left side of an\n> equals, but we would segfault if there was no content at\n> all.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  config.c               |    2 ++\n>  t/t1300-repo-config.sh |    4 ++++\n>  2 files changed, 6 insertions(+), 0 deletions(-)\n>\n> diff --git a/config.c b/config.c\n> index ebd404a..2517b62 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -46,6 +46,8 @@ static int git_config_parse_parameter(const char *text,\n>  \tstruct strbuf **pair;\n>  \tstrbuf_addstr(&tmp, text);\n>  \tpair = strbuf_split_max(&tmp, '=', 2);\n> +\tif (!pair[0])\n> +\t\treturn error(\"bogus config parameter: %s\", text);\n\nThis feels wrong.\n\nAsking strbuf_split() to split a string \"foo\" with \"=\" delimiter would\ngive you one element array (\"foo\", NULL), a string \"fo\" would give you\n(\"fo\", NULL), and a string \"f\" would give you (\"f\", NULL).  Shouldn't we\nget (\"\", NULL) if we ask it to split \"\"?\n"},{"id":"169961","messageId":"20110613192055.GE17845@sigill.intra.peff.net","threadId":"27577","inReplyTo":"7voc21od0g.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 01/10] strbuf_split: add a max parameter","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-13T19:20:55Z","receivedAt":"2011-06-13T19:20:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 13, 2011 at 10:30:07AM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > I am tempted to just call this new one strbuf_split and update all\n> > callers. There aren't that many.\n> \n> Yes, that is indeed tempting, and because we have a new parameter the\n> compiler will catch any new callers that pop up in a mismerge so that\n> would be perfectly safe.\n\nShould we also change the naming later in the series to remain\nconsistent with strbuf_add. IOW, to end up at:\n\n  struct strbuf **strbuf_split(const char *buf, int len, int delim, int max);\n  struct strbuf **strbuf_split_str(const char *s, int delim, int max);\n  struct strbuf **strbuf_split_buf(const struct strbuf *, int delim, int max);\n\n(though I think consistency would also dictate \"splitstr\" and \"splitbuf\"\nwithout the extra underscore. Personally I find it a bit unreadable).\n\n> > -struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n> > +struct strbuf **strbuf_split_max(const struct strbuf *sb, int delim, int max)\n> >  {\n> >  \tint alloc = 2, pos = 0;\n> >  \tchar *n, *p;\n> > @@ -114,7 +114,10 @@ struct strbuf **strbuf_split(const struct strbuf *sb, int delim)\n> >  \tp = n = sb->buf;\n> >  \twhile (n < sb->buf + sb->len) {\n> >  \t\tint len;\n> > -\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n> > +\t\tif (max <= 0 || pos + 1 < max)\n> > +\t\t\tn = memchr(n, delim, sb->len - (n - sb->buf));\n> > +\t\telse\n> > +\t\t\tn = NULL;\n> >  \t\tif (pos + 1 >= alloc) {\n> >  \t\t\talloc = alloc * 2;\n> >  \t\t\tret = xrealloc(ret, sizeof(struct strbuf *) * alloc);\n> \n> Hmm, even when we know the value of max, we go exponential, and even do so\n> by hand without using ALLOC_GROW(). Somewhat sad.\n\nThanks for reminding me. I noticed it wasn't using ALLOC_GROW, but\ndecided not to change it because I wanted to introduce an optimization\nlater on not to grow beyond max. But then I forgot. :)\n\nThe optimization I was going to do was to simply allocate \"max\" slots at\nthe beginning (if it's defined). You know you can't grow beyond that,\nand in most splits with a max, the caller is expecting all of them to be\nfilled.\n\nBut your two-pass patch below is also reasonable.\n\n> Also do we currently rely on the bug that strbuf_split() returns (NULL,)\n> instead of (\"\", NULL) when given an empty string?  If not, perhaps...\n\nI assumed that behavior was not a bug (and even had to avoid a segfault\nwith it in a later series, as you saw). But thinking on it more, it\nreally is one; splitting even a single character without delimiter ends\nup with a non-NULL portion, and I think the empty string should do the\nsame.\n\n>  strbuf.c |   50 +++++++++++++++++++++++++++++++-------------------\n>  1 files changed, 31 insertions(+), 19 deletions(-)\n\nI think your patch looks reasonable. In theory doing two passes over a\nvery large buffer (e.g., splitting lines from a large commit message)\nmight be slightly less efficient, but I imagine it is drowned out in the\nnoise of malloc'ing strbufs.\n\n> +\tfor (pass = 0; pass < 2; pass++) {\n> +\t\t/* First pass counts, second pass allocates and fills */\n\nMaybe it is just me, but I tend not to like writing multi-pass stuff\nlike this as a for-loop, but instead to factor it into a function with\nan \"actually allocate\" parameter. I find it makes the code much more\nobvious.\n\n> +\tif (!count) {\n>  \t\tt = xmalloc(sizeof(struct strbuf));\n> -\t\tstrbuf_init(t, len);\n> -\t\tstrbuf_add(t, p, len);\n> -\t\tret[pos] = t;\n> -\t\tret[++pos] = NULL;\n> -\t\tp = ++n;\n> +\t\tstrbuf_init(t, 0);\n> +\t\tret[0] = t;\n>  \t}\n\nI think my test in 4/10 (which avoids the segfault by checking\nexplicitly for NULL in the caller) should go with this part, and then\n4/10 can go away.\n\n-Peff\n"},{"id":"169962","messageId":"20110613192200.GF17845@sigill.intra.peff.net","threadId":"27577","inReplyTo":"7vhb7tod01.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 04/10] config: avoid segfault when parsing command-line config","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-06-13T19:22:00Z","receivedAt":"2011-06-13T19:22:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jun 13, 2011 at 10:30:22AM -0700, Junio C Hamano wrote:\n\n> > +\tif (!pair[0])\n> > +\t\treturn error(\"bogus config parameter: %s\", text);\n> \n> This feels wrong.\n> \n> Asking strbuf_split() to split a string \"foo\" with \"=\" delimiter would\n> give you one element array (\"foo\", NULL), a string \"fo\" would give you\n> (\"fo\", NULL), and a string \"f\" would give you (\"f\", NULL).  Shouldn't we\n> get (\"\", NULL) if we ask it to split \"\"?\n\nYeah, I was making the assumption that strbuf_split was not bugging, and\ncoding to its output. But I think you are right, that it is simply\nreturning bogus output.\n\n-Peff\n"}]}