{"thread":{"id":"8906","subject":"[RFC/PATCH] git-branch: default to --track","startedAt":"2007-07-06T21:54:09Z","lastAt":"2007-07-10T21:09:45Z","messageCount":23,"participants":["Johannes Schindelin","Junio C Hamano","Paolo Bonzini","Daniel Barkalow"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"46662","messageId":"Pine.LNX.4.64.0707062252390.4093@racer.site","threadId":"8906","inReplyTo":null,"subject":"[RFC/PATCH] git-branch: default to --track","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-06T21:54:09Z","receivedAt":"2007-07-06T21:54:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\n\"git branch --track\" will setup config variables when branching from \na remote branch, so that if you say \"git pull\" while being on that\nbranch, it automatically fetches the correct remote, and merges the\ncorrect branch.\n\nOften people complain that this is not the default for \"git branch\". \nMake it so.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\n---\n\n\tWith 1.5.3 knocking at the door, maybe it is too late to include\n\tthis. However, I am in favour of changing the default behaviour\n\there.\n\n builtin-branch.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex ae450b0..507b47c 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =\n static const char *head;\n static unsigned char head_sha1[20];\n \n-static int branch_track_remotes;\n+static int branch_track_remotes = 1;\n \n static int branch_use_color;\n static char branch_colors[][COLOR_MAXLEN] = {\n"},{"id":"46740","messageId":"7vhcof2rur.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707062252390.4093@racer.site","subject":"Re: [RFC/PATCH] git-branch: default to --track","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-08T08:59:40Z","receivedAt":"2007-07-08T08:59:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \"git branch --track\" will setup config variables when branching from \n> a remote branch, so that if you say \"git pull\" while being on that\n> branch, it automatically fetches the correct remote, and merges the\n> correct branch.\n\nWhile I think it would have been the right thing to do if the\ncode did this only for a remote branch, I think there is a bug\nsomewhere.  I just saw this:\n\n\t... some random changes ...\n        master$ git commit -a -s -m 'Some work meant for topic.'\n        master$ git branch jc/new-topic\n\tBranch jc/new-topic set up to track local branch refs/heads/master\n\nEh?  I did not want this to get applied for my local branches.\n\nThe intention of the above command sequence was to do a branch\nand then \"reset --hard HEAD^\" to rewind the 'master', as if I\ndid not commit but instead did \"checkout -b jc/new-topic &&\ncommit && checkout master\".\n\nBut \"checkout -b jc/newtopic\" has the same problem, as it\neventually uses the same \"git-branch\" that defaults to --track\neven for a case where I branch off of a local branch.\n\nI do not necessarily think the command line --track is broken.\nIf the user explicitly says a branch tracks a local branch, so\nbe it.  If --track comes from autosetupmerge or built-in default\nlike your patch, however, I do not think it makes much sense to\npollute the config file with useless \"tracking\" information.\n\nI am very tempted to revert this, but won't do so tonight, yet.\n"},{"id":"46750","messageId":"Pine.LNX.4.64.0707081336020.4248@racer.site","threadId":"8906","inReplyTo":"7vhcof2rur.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-08T12:41:21Z","receivedAt":"2007-07-08T12:41:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nJunio noticed that switching on autosetupmerge unilaterally started\ncluttering the config for local branches.  That is not the original\nintention of branch.autosetupmerge, which was meant purely for\nconvenience when branching off of remote branches, but that semantics\ngot lost somewhere.\n\nIf you still want that \"new\" behavior, you can switch\nbranch.autosetupmerge to the value \"all\".  Otherwise, it is interpreted\nas a boolean, which triggers setting up defaults _only_ when branching\noff of a remote branch, i.e. the originally intended behavior.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tOn Sun, 8 Jul 2007, Junio C Hamano wrote:\n\n\t> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\t> \n\t> > \"git branch --track\" will setup config variables when \n\t> > branching from a remote branch, so that if you say \"git pull\" \n\t> > while being on that branch, it automatically fetches the \n\t> > correct remote, and merges the correct branch.\n\t> \n\t> While I think it would have been the right thing to do if the\n\t> code did this only for a remote branch, I think there is a bug\n\t> somewhere.  I just saw this:\n\t> \n\t> \t... some random changes ...\n\t>         master$ git commit -a -s -m 'Some work meant for topic.'\n\t>         master$ git branch jc/new-topic\n\t> \tBranch jc/new-topic set up to track local branch refs/heads/master\n\t> \n\t> Eh?  I did not want this to get applied for my local branches.\n\n\tThat is certainly unexpected and unwelcomed.  Alas, I think it is \n\tone of the consequences of rarely executed (and thus, tested) \n\tcode.\n\n\tI rarely branch, but use one long running branch to commit and \n\trevert, so that a \"git log -S<keyword> -p\" brings me back my huge \n\tdebug output changes, and therefore I did not catch it.\n\n\t> I do not necessarily think the command line --track is broken.\n\n\tMe, neither.  Therefore, this patch does not change the semantics \n\tof that one.  But it was really unexpected for me to see that this \n\tworks with anything but remote branches.\n\n\t> I am very tempted to revert this, but won't do so tonight, yet.\n\n\tWell, I marked it as RFC, and was surprised to wake up to it being \n\tapplied.  But thanks for not reverting right away; I think this \n\tpatch should fix the issue.\n\n builtin-branch.c  |   18 ++++++++++++------\n t/t3200-branch.sh |    9 +++++++++\n 2 files changed, 21 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 507b47c..49195a1 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =\n static const char *head;\n static unsigned char head_sha1[20];\n \n-static int branch_track_remotes = 1;\n+static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */\n \n static int branch_use_color;\n static char branch_colors[][COLOR_MAXLEN] = {\n@@ -66,8 +66,12 @@ static int git_branch_config(const char *var, const char *value)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(var, \"branch.autosetupmerge\"))\n-\t\tbranch_track_remotes = git_config_bool(var, value);\n+\tif (!strcmp(var, \"branch.autosetupmerge\")) {\n+\t\tif (!strcmp(value, \"all\"))\n+\t\t\tbranch_track = 2;\n+\t\telse\n+\t\t\tbranch_track = git_config_bool(var, value);\n+\t}\n \n \treturn git_default_config(var, value);\n }\n@@ -525,7 +529,9 @@ static void create_branch(const char *name, const char *start_name,\n \t/* When branching off a remote branch, set up so that git-pull\n \t   automatically merges from there.  So far, this is only done for\n \t   remotes registered via .git/config.  */\n-\tif (real_ref && track)\n+\tif (real_ref && (track == 2 ||\n+\t\t\t\t(track == 1 &&\n+\t\t\t\t !prefixcmp(real_ref, \"refs/remotes/\"))))\n \t\tset_branch_defaults(name, real_ref);\n \n \tif (write_ref_sha1(lock, sha1, msg) < 0)\n@@ -586,7 +592,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tint i;\n \n \tgit_config(git_branch_config);\n-\ttrack = branch_track_remotes;\n+\ttrack = branch_track;\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -598,7 +604,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tbreak;\n \t\t}\n \t\tif (!strcmp(arg, \"--track\")) {\n-\t\t\ttrack = 1;\n+\t\t\ttrack = 2;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--no-track\")) {\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex c6f472a..a19e961 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -148,6 +148,15 @@ test_expect_success 'test tracking setup via config' \\\n      test $(git config branch.my3.remote) = local &&\n      test $(git config branch.my3.merge) = refs/heads/master'\n \n+test_expect_success 'autosetupmerge = all' '\n+\tgit config branch.autosetupmerge true &&\n+\tgit branch all1 master &&\n+\ttest -z \"$(git config branch.all1.merge)\" &&\n+\tgit config branch.autosetupmerge all &&\n+\tgit branch all2 master &&\n+\ttest $(git config branch.all2.merge) = refs/heads/master\n+'\n+\n test_expect_success 'test overriding tracking setup via --no-track' \\\n     'git config branch.autosetupmerge true &&\n      git config remote.local.url . &&\n-- \n1.5.3.rc0.2742.g2050\n"},{"id":"46776","messageId":"7vzm2620wp.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707081336020.4248@racer.site","subject":"Re: [PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-08T18:41:42Z","receivedAt":"2007-07-08T18:41:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \t> Eh?  I did not want this to get applied for my local branches.\n>\n> \tThat is certainly unexpected and unwelcomed.  Alas, I think it is \n> \tone of the consequences of rarely executed (and thus, tested) \n> \tcode.\n> ...\n> +test_expect_success 'autosetupmerge = all' '\n> +\tgit config branch.autosetupmerge true &&\n> +\tgit branch all1 master &&\n> +\ttest -z \"$(git config branch.all1.merge)\" &&\n> +\tgit config branch.autosetupmerge all &&\n> +\tgit branch all2 master &&\n> +\ttest $(git config branch.all2.merge) = refs/heads/master\n> +'\n\nThanks.\n\nHaving prepared the patch below, I do not think if the original\npatch even wanted to have 'all' semantics.  The surrounding text\nonly talks about \"off a remote branch\" and I strongly suspect\nthat nobody wanted to do this for a local branch case at all.\n\n\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4b67f0a..aeece84 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -309,7 +309,10 @@ branch.autosetupmerge::\n \tso that gitlink:git-pull[1] will appropriately merge from that\n \tremote branch.  Note that even if this option is not set,\n \tthis behavior can be chosen per-branch using the `--track`\n-\tand `--no-track` options.  This option defaults to false.\n+\tand `--no-track` options.  This option can have values\n+\t'false' (never touch the configuration), 'all' (do this\n+\tfor all branches), or 'true' (do this only when\n+\tbranching from a remote tracking branch), and defaults to 'true'.\n \n branch.<name>.remote::\n \tWhen in branch <name>, it tells `git fetch` which remote to fetch.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 818b720..8292952 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -52,8 +52,9 @@ OPTIONS\n \tset up configuration so that git-pull will automatically\n \tretrieve data from the remote branch.  Set the\n \tbranch.autosetupmerge configuration variable to true if you\n-\twant git-checkout and git-branch to always behave as if\n-\t'--track' were given.\n+\twant git-checkout and git-branch to behave as if\n+\t'--track' were given when you branch from a remote\n+\ttracking branch.\n \n --no-track::\n \tWhen -b is given and a branch is created off a remote branch,\n"},{"id":"46781","messageId":"Pine.LNX.4.64.0707082014140.4248@racer.site","threadId":"8906","inReplyTo":"7vzm2620wp.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-08T19:15:21Z","receivedAt":"2007-07-08T19:15:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 8 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > \t> Eh?  I did not want this to get applied for my local branches.\n> >\n> > \tThat is certainly unexpected and unwelcomed.  Alas, I think it is \n> > \tone of the consequences of rarely executed (and thus, tested) \n> > \tcode.\n> > ...\n> > +test_expect_success 'autosetupmerge = all' '\n> > +\tgit config branch.autosetupmerge true &&\n> > +\tgit branch all1 master &&\n> > +\ttest -z \"$(git config branch.all1.merge)\" &&\n> > +\tgit config branch.autosetupmerge all &&\n> > +\tgit branch all2 master &&\n> > +\ttest $(git config branch.all2.merge) = refs/heads/master\n> > +'\n> \n> Thanks.\n> \n> Having prepared the patch below, I do not think if the original\n> patch even wanted to have 'all' semantics.  The surrounding text\n> only talks about \"off a remote branch\" and I strongly suspect\n> that nobody wanted to do this for a local branch case at all.\n\nI remember that the comment was correct for the first few versions.  \nSomehow I missed that change in semantics.  Paolo, what was the rationale?\n\nCiao,\nDscho\n"},{"id":"46816","messageId":"46919692.5020708@gnu.org","threadId":"8906","inReplyTo":"7vzm2620wp.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2007-07-09T01:59:46Z","receivedAt":"2007-07-09T01:59:46Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"\t> Having prepared the patch below, I do not think if the original\n> patch even wanted to have 'all' semantics.  The surrounding text\n> only talks about \"off a remote branch\" and I strongly suspect\n> that nobody wanted to do this for a local branch case at all.\n\nIf I remember correctly, the problem was that you are not sure that\nremote branches are in refs/remotes.\n\nPaolo\n"},{"id":"46817","messageId":"7vhcoexqeh.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"46919692.5020708@gnu.org","subject":"Re: [PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-09T02:27:34Z","receivedAt":"2007-07-09T02:27:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n> \t> Having prepared the patch below, I do not think if the original\n>> patch even wanted to have 'all' semantics.  The surrounding text\n>> only talks about \"off a remote branch\" and I strongly suspect\n>> that nobody wanted to do this for a local branch case at all.\n>\n> If I remember correctly, the problem was that you are not sure that\n> remote branches are in refs/remotes.\n\nYes, the user can use traditional layout (e.g. refs/heads/origin\nis used as a remote tracking branch).\n\nSo the check with refs/remotes/ is not technically correct, but\nit should probably look-up the configuration to check the\ntracking, if we really want to be strict about it.\n\nI personally do not care too much about it, though.\n"},{"id":"46852","messageId":"Pine.LNX.4.64.0707091025250.4248@racer.site","threadId":"8906","inReplyTo":"46919692.5020708@gnu.org","subject":"Re: [PATCH] branch.autosetupmerge: allow boolean values, or \"all\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-09T11:28:05Z","receivedAt":"2007-07-09T11:28:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 8 Jul 2007, Paolo Bonzini wrote:\n\n> [Paolo tried to hide the fact that it was Junio who wrote this:]\n>\n> > Having prepared the patch below, I do not think if the original patch \n> > even wanted to have 'all' semantics.  The surrounding text only talks \n> > about \"off a remote branch\" and I strongly suspect that nobody wanted \n> > to do this for a local branch case at all.\n> \n> If I remember correctly, the problem was that you are not sure that \n> remote branches are in refs/remotes.\n\nThen you code is incorrect.\n\nBasically, you use a confusing set of four functions to do the following:\n\n- read the config, and\n- write the branch.<name>.{remote,merge} variables\n\nTwo functions would have been sufficient, and easier to read.  And as I \nfully expect with non-simple code, a bug was lurking.  This time in \nset_branch_defaults():\n\nyou check if neither config_repo nor config_remote (which is a misnomer, \nas it does not contain a \"remote\", but a \"remote branch\") is set.  But \nthat happens when there was no information in the config, too!\n\nAlso you miss the case that there is ambiguous information:\n\n[remote \"hello\"]\n\turl = git://blub/x.git\n\tfetch = refs/heads/master:refs/heads/origin\n\n[remote \"bello\"]\n\turl = git://yaddayadda/x.git\n\tfetch = refs/heads/master:refs/heads/origin\n\nSee? Your code just uses \"bello\".\n\nWill send out a fix shortly.\n\nCiao,\nDscho\n"},{"id":"46854","messageId":"Pine.LNX.4.64.0707091228290.5546@racer.site","threadId":"8906","inReplyTo":"7vhcoexqeh.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] branch --track: code cleanup and saner handling of local branches","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-09T11:35:05Z","receivedAt":"2007-07-09T11:35:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch cleans up some complicated code, and replaces it with a\ncleaner version.  This also enables us to fix two cases:\n\nThe earlier \"fix\" to setup tracking only when the original ref started\nwith \"refs/remotes\" is wrong.  You are absolutely allowed to use a\nseparate layout for your tracking branches.  The correct fix, of course,\nis to set up tracking information only when there is a matching\nremote.<nick>.fetch line containing a colon.\n\nAnother corner case was not handled properly.  If two remotes write to\nthe original ref, just warn the user and do not set up tracking.\n\nAlso, the \"branch name too long\" condition had an off-by-one.  Not that it \nmatters in real life.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tOn Sun, 8 Jul 2007, Junio C Hamano wrote:\n\n\t> Paolo Bonzini <bonzini@gnu.org> writes:\n\t> \n\t> >> Having prepared the patch below, I do not think if the \n\t> >> original patch even wanted to have 'all' semantics.  The \n\t> >> surrounding text only talks about \"off a remote branch\" and I \n\t> >> strongly suspect that nobody wanted to do this for a local \n\t> >> branch case at all.\n\t> >\n\t> > If I remember correctly, the problem was that you are not sure \n\t> > that remote branches are in refs/remotes.\n\t> \n\t> Yes, the user can use traditional layout (e.g. refs/heads/origin\n\t> is used as a remote tracking branch).\n\t> \n\t> So the check with refs/remotes/ is not technically correct, but\n\t> it should probably look-up the configuration to check the\n\t> tracking, if we really want to be strict about it.\n\n\tOkay, so here is the correct fix.  In the process, I rewrote large \n\tparts of it.  I really did not like the asnprintf() parts of the \n\toriginal part, and I am not comfortable with it anyway, so that is \n\treplaced, too.\n\n\tBTW if someone wonders why the wildcards look so strange in the \n\tcomment: after the second compilation I got sick of the \"warning: \n\t/* contained within comment\", and wanted to spare everybody.\n\n\tAh yes, I tried to prepare this patch with \"format-patch -B\", but \n\tit seems to be too much in love with the few lines that match. \n\n builtin-branch.c  |  196 ++++++++++++++++++++++++-----------------------------\n t/t3200-branch.sh |   21 +++---\n 2 files changed, 99 insertions(+), 118 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 49195a1..d290a7a 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -22,7 +22,7 @@ static const char builtin_branch_usage[] =\n static const char *head;\n static unsigned char head_sha1[20];\n \n-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */\n+static int branch_track = 1;\n \n static int branch_use_color;\n static char branch_colors[][COLOR_MAXLEN] = {\n@@ -66,12 +66,8 @@ static int git_branch_config(const char *var, const char *value)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(var, \"branch.autosetupmerge\")) {\n-\t\tif (!strcmp(value, \"all\"))\n-\t\t\tbranch_track = 2;\n-\t\telse\n+\tif (!strcmp(var, \"branch.autosetupmerge\"))\n \t\t\tbranch_track = git_config_bool(var, value);\n-\t}\n \n \treturn git_default_config(var, value);\n }\n@@ -349,125 +345,111 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,\n \tfree_ref_list(&ref_list);\n }\n \n-static char *config_repo;\n-static char *config_remote;\n-static const char *start_ref;\n+static struct {\n+\tconst char *ref;\n+\tint ref_len;\n+\tchar *remote;\n+\tchar *merge;\n+\tint matches;\n+} tracking;\n \n-static int get_remote_branch_name(const char *value)\n+static int tracking_config(const char *key, const char *value)\n {\n \tconst char *colon;\n-\tconst char *end;\n-\n-\tif (*value == '+')\n-\t\tvalue++;\n+\tint key_len, value_len, match_len;\n+\tchar *merge = NULL;\n \n-\tcolon = strchr(value, ':');\n-\tif (!colon)\n+\t/* we want: remote.<nick>.fetch = <merge>:<ref> */\n+\tif (prefixcmp(key, \"remote.\") || (key_len = strlen(key)) < 6 ||\n+\t\t\tstrcmp(key + key_len - 6, \".fetch\") ||\n+\t\t\t!(colon = strchr(value, ':')))\n \t\treturn 0;\n \n-\tend = value + strlen(value);\n+\tif (*value == '+')\n+\t\tvalue++;\n \n \t/*\n-\t * Try an exact match first.  I.e. handle the case where the\n-\t * value is \"$anything:refs/foo/bar/baz\" and start_ref is exactly\n-\t * \"refs/foo/bar/baz\". Then the name at the remote is $anything.\n+\t * A remote.<name>.fetch value can have two forms:\n+\t *\n+\t * - exact:\n+\t *\n+\t *\trefs/heads/gnu:refs/heads/my-upstream\n+\t *\n+\t * - wildcard:\n+\t *\n+\t *\trefs/heads/ *:refs/remotes/gnu/ *\n+\t *\n+\t * try exact match first:\n \t */\n-\tif (!strcmp(colon + 1, start_ref)) {\n+\tif (!strcmp(colon + 1, tracking.ref))\n \t\t/* Truncate the value before the colon. */\n-\t\tnfasprintf(&config_repo, \"%.*s\", colon - value, value);\n-\t\treturn 1;\n+\t\tmerge = xstrndup(value, colon - value);\n+\n+\t/* wildcard; match_len is the length of the matching prefix */\n+\telse if ((value_len = strlen(value)) > tracking.ref_len &&\n+\t\t\t!strcmp(value + value_len - 2, \"/*\") &&\n+\t\t\t(match_len = value_len - (colon - value) - 2) > 0 &&\n+\t\t\tmatch_len < tracking.ref_len &&\n+\t\t\t!memcmp(colon + 1, tracking.ref, match_len)) {\n+\t\tint postfix_len = tracking.ref_len - match_len;\n+\t\tint replace_len = colon - 1 - value;\n+\t\tmerge = xmalloc(replace_len + postfix_len);\n+\t\tmemcpy(merge, value, replace_len);\n+\t\tmemcpy(merge + replace_len,\n+\t\t\t\ttracking.ref + match_len, postfix_len);\n \t}\n \n-\t/*\n-\t * Is this a wildcard match?\n-\t */\n-\tif ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||\n-\t    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')\n-\t\treturn 0;\n-\n-\t/*\n-\t * Value is \"refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>\"\n-\t * and start_ref begins with \"refs/baz/boa/\"; the name at the\n-\t * remote is refs/foo/bar/ with the remaining part of the\n-\t * start_ref.  The length of the prefix on the RHS is (end -\n-\t * colon - 2), including the slash immediately before the\n-\t * asterisk.\n-\t */\n-\tif ((strlen(start_ref) < end - colon - 2) ||\n-\t    memcmp(start_ref, colon + 1, end - colon - 2))\n-\t\treturn 0; /* does not match prefix */\n-\n-\t/* Replace the asterisk with the remote branch name.  */\n-\tnfasprintf(&config_repo, \"%.*s%s\",\n-\t\t   (colon - 1) - value, value,\n-\t\t   start_ref + (end - colon - 2));\n-\treturn 1;\n-}\n-\n-static int get_remote_config(const char *key, const char *value)\n-{\n-\tconst char *var;\n-\tif (prefixcmp(key, \"remote.\"))\n+\tif (!merge)\n \t\treturn 0;\n \n-\tvar = strrchr(key, '.');\n-\tif (var == key + 6 || strcmp(var, \".fetch\"))\n-\t\treturn 0;\n-\t/*\n-\t * Ok, we are looking at key == \"remote.$foo.fetch\";\n-\t */\n-\tif (get_remote_branch_name(value))\n-\t\tnfasprintf(&config_remote, \"%.*s\", var - (key + 7), key + 7);\n+\ttracking.matches++;\n+\tif (tracking.merge)\n+\t\tfree(merge);\n+\telse {\n+\t\ttracking.merge = merge;\n+\t\ttracking.remote = xstrndup(key + 7, key_len - 7 - 6);\n+\t}\n \n \treturn 0;\n }\n \n-static void set_branch_merge(const char *name, const char *config_remote,\n-\t\t\t     const char *config_repo)\n-{\n-\tchar key[1024];\n-\tif (sizeof(key) <=\n-\t    snprintf(key, sizeof(key), \"branch.%s.remote\", name))\n-\t\tdie(\"what a long branch name you have!\");\n-\tgit_config_set(key, config_remote);\n \n-\t/*\n-\t * We do not have to check if we have enough space for\n-\t * the 'merge' key, since it's shorter than the\n-\t * previous 'remote' key, which we already checked.\n-\t */\n-\tsnprintf(key, sizeof(key), \"branch.%s.merge\", name);\n-\tgit_config_set(key, config_repo);\n-}\n-\n-static void set_branch_defaults(const char *name, const char *real_ref)\n+/*\n+ * This is called when new_ref is branched off of orig_ref, and tries\n+ * to infer the settings for branch.<new_ref>.{remote,merge} from the\n+ * config.\n+ */\n+static void setup_tracking(const char *new_ref, const char *orig_ref)\n {\n-\t/*\n-\t * name is the name of new branch under refs/heads;\n-\t * real_ref is typically refs/remotes/$foo/$bar, where\n-\t * $foo is the remote name (there typically are no slashes)\n-\t * and $bar is the branch name we map from the remote\n-\t * (it could have slashes).\n-\t */\n-\tstart_ref = real_ref;\n-\tgit_config(get_remote_config);\n-\tif (!config_repo && !config_remote &&\n-\t    !prefixcmp(real_ref, \"refs/heads/\")) {\n-\t\tset_branch_merge(name, \".\", real_ref);\n-\t\tprintf(\"Branch %s set up to track local branch %s.\\n\",\n-\t\t       name, real_ref);\n-\t}\n-\n-\tif (config_repo && config_remote) {\n-\t\tset_branch_merge(name, config_remote, config_repo);\n-\t\tprintf(\"Branch %s set up to track remote branch %s.\\n\",\n-\t\t       name, real_ref);\n+\ttracking.ref = orig_ref;\n+\ttracking.ref_len = strlen(tracking.ref);\n+\tgit_config(tracking_config);\n+\n+\tif (tracking.matches > 1)\n+\t\terror(\"Not tracking: ambiguous information for ref %s\",\n+\t\t\t\torig_ref);\n+\telse if (tracking.matches == 1) {\n+\t\tchar key[1024];\n+\t\tint n = snprintf(key, sizeof(key), \"branch.%s.remote\",\n+\t\t\t\tnew_ref);\n+\t\tif (n < sizeof(key) - 1) {\n+\t\t\tgit_config_set(key, tracking.remote ?\n+\t\t\t\t\ttracking.remote : \".\");\n+\n+\t\t\tsnprintf(key, sizeof(key), \"branch.%s.merge\", new_ref);\n+\t\t\tgit_config_set(key, tracking.merge);\n+\n+\t\t\tprintf(\"Branch %s set up to track remote branch %s.\\n\",\n+\t\t\t       new_ref, orig_ref);\n+\t\t} else\n+\t\t\terror(\"Tracking not set up: name too long: %s\",\n+\t\t\t\tnew_ref);\n \t}\n \n-\tif (config_repo)\n-\t\tfree(config_repo);\n-\tif (config_remote)\n-\t\tfree(config_remote);\n+\tif (tracking.remote)\n+\t\tfree(tracking.remote);\n+\tif (tracking.merge)\n+\t\tfree(tracking.merge);\n }\n \n static void create_branch(const char *name, const char *start_name,\n@@ -529,10 +511,8 @@ static void create_branch(const char *name, const char *start_name,\n \t/* When branching off a remote branch, set up so that git-pull\n \t   automatically merges from there.  So far, this is only done for\n \t   remotes registered via .git/config.  */\n-\tif (real_ref && (track == 2 ||\n-\t\t\t\t(track == 1 &&\n-\t\t\t\t !prefixcmp(real_ref, \"refs/remotes/\"))))\n-\t\tset_branch_defaults(name, real_ref);\n+\tif (real_ref && track)\n+\t\tsetup_tracking(name, real_ref);\n \n \tif (write_ref_sha1(lock, sha1, msg) < 0)\n \t\tdie(\"Failed to write ref: %s.\", strerror(errno));\n@@ -604,7 +584,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tbreak;\n \t\t}\n \t\tif (!strcmp(arg, \"--track\")) {\n-\t\t\ttrack = 2;\n+\t\t\ttrack = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--no-track\")) {\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex a19e961..ef1eeb7 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \\\n      test $(git config branch.my3.remote) = local &&\n      test $(git config branch.my3.merge) = refs/heads/master'\n \n-test_expect_success 'autosetupmerge = all' '\n+test_expect_success 'avoid ambiguous track' '\n \tgit config branch.autosetupmerge true &&\n+\tgit config remote.ambi1.url = lalala &&\n+\tgit config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&\n+\tgit config remote.ambi2.url = lilili &&\n+\tgit config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&\n \tgit branch all1 master &&\n-\ttest -z \"$(git config branch.all1.merge)\" &&\n-\tgit config branch.autosetupmerge all &&\n-\tgit branch all2 master &&\n-\ttest $(git config branch.all2.merge) = refs/heads/master\n+\ttest -z \"$(git config branch.all1.merge)\"\n '\n \n test_expect_success 'test overriding tracking setup via --no-track' \\\n@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \\\n      ! test \"$(git config branch.my2.remote)\" = local &&\n      ! test \"$(git config branch.my2.merge)\" = refs/heads/master'\n \n-test_expect_success 'test local tracking setup' \\\n+test_expect_success 'no tracking without .fetch entries' \\\n     'git branch --track my6 s &&\n-     test $(git config branch.my6.remote) = . &&\n-     test $(git config branch.my6.merge) = refs/heads/s'\n+     test -z \"$(git config branch.my6.remote)\" &&\n+     test -z \"$(git config branch.my6.merge)\"'\n \n test_expect_success 'test tracking setup via --track but deeper' \\\n     'git config remote.local.url . &&\n@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \\\n \n test_expect_success 'test deleting branch deletes branch config' \\\n     'git branch -d my7 &&\n-     test \"$(git config branch.my7.remote)\" = \"\" &&\n-     test \"$(git config branch.my7.merge)\" = \"\"'\n+     test -z \"$(git config branch.my7.remote)\" &&\n+     test -z \"$(git config branch.my7.merge)\"'\n \n test_expect_success 'test deleting branch without config' \\\n     'git branch my7 s &&\n-- \n1.5.3.rc0.2769.gd9be2\n"},{"id":"46892","messageId":"Pine.LNX.4.64.0707092203100.5546@racer.site","threadId":"8906","inReplyTo":"7v4pkduw2f.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] branch --track: code cleanup and saner handling of local branches","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-09T21:05:14Z","receivedAt":"2007-07-09T21:05:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 9 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > -static int get_remote_branch_name(const char *value)\n> > +static int tracking_config(const char *key, const char *value)\n> > [...]\n> >  \t/*\n> > -\t * Try an exact match first.  I.e. handle the case where the\n> > -\t * value is \"$anything:refs/foo/bar/baz\" and start_ref is exactly\n> > -\t * \"refs/foo/bar/baz\". Then the name at the remote is $anything.\n> > +\t * A remote.<name>.fetch value can have two forms:\n> > +\t *\n> > +\t * - exact:\n> > +\t *\n> > +\t *\trefs/heads/gnu:refs/heads/my-upstream\n> > +\t *\n> > +\t * - wildcard:\n> > +\t *\n> > +\t *\trefs/heads/ *:refs/remotes/gnu/ *\n> > +\t *\n> > +\t * try exact match first:\n> >  \t */\n> \n> It strikes me a bit odd if Daniel's remote.[ch] infrastructure\n> does not give you easy access to this kind of information...\n\nYes, probably.  However, at the time he was sending that patch, I was \nalready preparing the patch.  Besides, we can always go back and change \nthe code, if you really want to pull in the remotes stuff into \nbuiltin-branch (as of yet, they are almost independent).\n\nMy vote is to have it indpendent for now, if only to fix existing issues.  \nBut in the end it is your choice.\n\nCiao,\nDscho\n"},{"id":"46891","messageId":"7v4pkduw2f.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707091228290.5546@racer.site","subject":"Re: [PATCH] branch --track: code cleanup and saner handling of local branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-09T21:05:44Z","receivedAt":"2007-07-09T21:05:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> @@ -349,125 +345,111 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,\n>  \tfree_ref_list(&ref_list);\n>  }\n>  \n> -static char *config_repo;\n> -static char *config_remote;\n> -static const char *start_ref;\n> +static struct {\n> +\tconst char *ref;\n> +\tint ref_len;\n> +\tchar *remote;\n> +\tchar *merge;\n> +\tint matches;\n> +} tracking;\n>  \n> -static int get_remote_branch_name(const char *value)\n> +static int tracking_config(const char *key, const char *value)\n>  {\n>  \tconst char *colon;\n> +\tint key_len, value_len, match_len;\n> +\tchar *merge = NULL;\n>  \n> -\tcolon = strchr(value, ':');\n> -\tif (!colon)\n> +\t/* we want: remote.<nick>.fetch = <merge>:<ref> */\n> +\tif (prefixcmp(key, \"remote.\") || (key_len = strlen(key)) < 6 ||\n> +\t\t\tstrcmp(key + key_len - 6, \".fetch\") ||\n> +\t\t\t!(colon = strchr(value, ':')))\n>  \t\treturn 0;\n>  \n> -\tend = value + strlen(value);\n> +\tif (*value == '+')\n> +\t\tvalue++;\n>  \n>  \t/*\n> -\t * Try an exact match first.  I.e. handle the case where the\n> -\t * value is \"$anything:refs/foo/bar/baz\" and start_ref is exactly\n> -\t * \"refs/foo/bar/baz\". Then the name at the remote is $anything.\n> +\t * A remote.<name>.fetch value can have two forms:\n> +\t *\n> +\t * - exact:\n> +\t *\n> +\t *\trefs/heads/gnu:refs/heads/my-upstream\n> +\t *\n> +\t * - wildcard:\n> +\t *\n> +\t *\trefs/heads/ *:refs/remotes/gnu/ *\n> +\t *\n> +\t * try exact match first:\n>  \t */\n\nIt strikes me a bit odd if Daniel's remote.[ch] infrastructure\ndoes not give you easy access to this kind of information...\n"},{"id":"46894","messageId":"7vzm25tex6.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707092203100.5546@racer.site","subject":"Re: [PATCH] branch --track: code cleanup and saner handling of local branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-09T22:01:25Z","receivedAt":"2007-07-09T22:01:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> It strikes me a bit odd if Daniel's remote.[ch] infrastructure\n>> does not give you easy access to this kind of information...\n>\n> Yes, probably.  However, at the time he was sending that patch, I was \n> already preparing the patch.\n\nI was talking about existing remote.[ch] patches, which has been\nin tree since late May this year, not his recent round of\n\"approach to builtin git-fetch\" series.\n"},{"id":"46906","messageId":"Pine.LNX.4.64.0707100401070.4131@racer.site","threadId":"8906","inReplyTo":"7vzm25tex6.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T03:02:02Z","receivedAt":"2007-07-10T03:02:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe function for_each_remote() does exactly what the name suggests.\n\nThe function remote_find_tracking() was extended to be able to search\nremote refs for a given local ref.  You have to set the parameter\n\"reverse\" to true for that behavior.\n\nBoth changes are required for the next step: simplification of\ngit-branch's --track functionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tYou're right. I completely missed that functionality. Well, a\n\tfew tweaks were needed. If this clashes too seriously with\n\tDaniel's work, I will gladly redo it after his changes are\n\tin \"next\".\n\n remote.c    |   42 ++++++++++++++++++++++++++++++++----------\n remote.h    |    7 ++++++-\n send-pack.c |    3 +--\n 3 files changed, 39 insertions(+), 13 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex cf98a44..21adb0d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -279,6 +279,26 @@ struct remote *remote_get(const char *name)\n \treturn ret;\n }\n \n+int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\tint i, result = 0;\n+\tread_config();\n+\tfor (i = 0; i < allocated_remotes; i++) {\n+\t\tstruct remote *r = remotes[i];\n+\t\tif (!r)\n+\t\t\tcontinue;\n+\t\tif (!r->fetch)\n+\t\t\tr->fetch = parse_ref_spec(r->fetch_refspec_nr,\n+\t\t\t\t\tr->fetch_refspec);\n+\t\tif (!r->push)\n+\t\t\tr->push = parse_ref_spec(r->push_refspec_nr,\n+\t\t\t\t\tr->push_refspec);\n+\t\tif ((result = fn(r, priv)))\n+\t\t\tbreak;\n+\t}\n+\treturn result;\n+}\n+\n int remote_has_uri(struct remote *remote, const char *uri)\n {\n \tint i;\n@@ -289,34 +309,36 @@ int remote_has_uri(struct remote *remote, const char *uri)\n \treturn 0;\n }\n \n-int remote_find_tracking(struct remote *remote, struct refspec *refspec)\n+int remote_find_tracking(struct remote *remote, struct refspec *refspec,\n+\t\tint reverse)\n {\n \tint i;\n \tfor (i = 0; i < remote->fetch_refspec_nr; i++) {\n \t\tstruct refspec *fetch = &remote->fetch[i];\n+\t\tconst char *src = reverse ? fetch->dst : fetch->src;\n+\t\tconst char *dst = reverse ? fetch->src : fetch->dst;\n \t\tif (!fetch->dst)\n \t\t\tcontinue;\n \t\tif (fetch->pattern) {\n-\t\t\tif (!prefixcmp(refspec->src, fetch->src)) {\n+\t\t\tif (!prefixcmp(refspec->src, src)) {\n \t\t\t\trefspec->dst =\n-\t\t\t\t\txmalloc(strlen(fetch->dst) +\n+\t\t\t\t\txmalloc(strlen(dst) +\n \t\t\t\t\t\tstrlen(refspec->src) -\n-\t\t\t\t\t\tstrlen(fetch->src) + 1);\n-\t\t\t\tstrcpy(refspec->dst, fetch->dst);\n-\t\t\t\tstrcpy(refspec->dst + strlen(fetch->dst),\n-\t\t\t\t       refspec->src + strlen(fetch->src));\n+\t\t\t\t\t\tstrlen(src) + 1);\n+\t\t\t\tstrcpy(refspec->dst, dst);\n+\t\t\t\tstrcpy(refspec->dst + strlen(dst),\n+\t\t\t\t       refspec->src + strlen(src));\n \t\t\t\trefspec->force = fetch->force;\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t} else {\n-\t\t\tif (!strcmp(refspec->src, fetch->src)) {\n-\t\t\t\trefspec->dst = xstrdup(fetch->dst);\n+\t\t\tif (!strcmp(refspec->src, src)) {\n+\t\t\t\trefspec->dst = xstrdup(dst);\n \t\t\t\trefspec->force = fetch->force;\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t}\n \t}\n-\trefspec->dst = NULL;\n \treturn -1;\n }\n \ndiff --git a/remote.h b/remote.h\nindex 01dbcef..9ab7eb6 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -20,6 +20,9 @@ struct remote {\n \n struct remote *remote_get(const char *name);\n \n+typedef int each_remote_fn(struct remote *remote, void *priv);\n+int for_each_remote(each_remote_fn fn, void *priv);\n+\n int remote_has_uri(struct remote *remote, const char *uri);\n \n struct refspec {\n@@ -35,7 +38,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,\n \n /*\n  * For the given remote, reads the refspec's src and sets the other fields.\n+ * If reverse is 1, the given src is the local ref, and we want the remote.\n  */\n-int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n+int remote_find_tracking(struct remote *remote, struct refspec *refspec,\n+\tint reverse);\n \n #endif\ndiff --git a/send-pack.c b/send-pack.c\nindex fecbda9..9fdd7b4 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -305,8 +305,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha\n \t\tif (remote) {\n \t\t\tstruct refspec rs;\n \t\t\trs.src = ref->name;\n-\t\t\tremote_find_tracking(remote, &rs);\n-\t\t\tif (rs.dst) {\n+\t\t\tif (!remote_find_tracking(remote, &rs, 0)) {\n \t\t\t\tstruct ref_lock *lock;\n \t\t\t\tfprintf(stderr, \" Also local %s\\n\", rs.dst);\n \t\t\t\tif (will_delete_ref) {\n-- \n1.5.3.rc0.2769.gd9be2\n"},{"id":"46908","messageId":"Pine.LNX.4.64.0707100403550.4131@racer.site","threadId":"8906","inReplyTo":"7vzm25tex6.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 2/2] branch --track: code cleanup and saner handling of local branches","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T03:05:56Z","receivedAt":"2007-07-10T03:05:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch cleans up some complicated code, and replaces it with a\ncleaner version, using code from remote.[ch], which got extended a\nlittle in the process.  This also enables us to fix two cases:\n\nThe earlier \"fix\" to setup tracking only when the original ref started\nwith \"refs/remotes\" is wrong.  You are absolutely allowed to use a\nseparate layout for your tracking branches.  The correct fix, of course,\nis to set up tracking information only when there is a matching\nremote.<nick>.fetch line containing a colon.\n\nAnother corner case was not handled properly.  If two remotes write to\nthe original ref, just warn the user and do not set up tracking.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\t... and again I was bitten by the minimality of the script. The\n\toriginal diff was _unreadable_, because it matched some empty \n\tlines here and there, or some lonely curly brackets.\n\n\tTherefore I manually tweaked it to show first the cruft that was \n\tremoved, and then the rewrite using remote.[ch]. As a consequence, \n\tthe diffstat is not the one of this patch, but the one \"git show \n\t--stat\" showed.\n\n builtin-branch.c  |  167 ++++++++++++++++------------------------------------\n t/t3200-branch.sh |   21 ++++---\n 2 files changed, 63 insertions(+), 125 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 49195a1..0dbd6d7 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"commit.h\"\n #include \"builtin.h\"\n+#include \"remote.h\"\n \n static const char builtin_branch_usage[] =\n   \"git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] [--sort-by-date]\";\n@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =\n static const char *head;\n static unsigned char head_sha1[20];\n \n-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */\n+static int branch_track = 1;\n \n static int branch_use_color;\n static char branch_colors[][COLOR_MAXLEN] = {\n@@ -66,12 +67,8 @@ static int git_branch_config(const char *var, const char *value)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(var, \"branch.autosetupmerge\")) {\n-\t\tif (!strcmp(value, \"all\"))\n-\t\t\tbranch_track = 2;\n-\t\telse\n+\tif (!strcmp(var, \"branch.autosetupmerge\"))\n \t\t\tbranch_track = git_config_bool(var, value);\n-\t}\n \n \treturn git_default_config(var, value);\n }\n@@ -349,125 +346,67 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,\n \tfree_ref_list(&ref_list);\n }\n \n-static char *config_repo;\n-static char *config_remote;\n-static const char *start_ref;\n-\n-static int get_remote_branch_name(const char *value)\n-{\n-\tconst char *colon;\n-\tconst char *end;\n-\n-\tif (*value == '+')\n-\t\tvalue++;\n-\n-\tcolon = strchr(value, ':');\n-\tif (!colon)\n-\t\treturn 0;\n-\n-\tend = value + strlen(value);\n-\n-\t/*\n-\t * Try an exact match first.  I.e. handle the case where the\n-\t * value is \"$anything:refs/foo/bar/baz\" and start_ref is exactly\n-\t * \"refs/foo/bar/baz\". Then the name at the remote is $anything.\n-\t */\n-\tif (!strcmp(colon + 1, start_ref)) {\n-\t\t/* Truncate the value before the colon. */\n-\t\tnfasprintf(&config_repo, \"%.*s\", colon - value, value);\n-\t\treturn 1;\n-\t}\n-\n-\t/*\n-\t * Is this a wildcard match?\n-\t */\n-\tif ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||\n-\t    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')\n-\t\treturn 0;\n-\n-\t/*\n-\t * Value is \"refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>\"\n-\t * and start_ref begins with \"refs/baz/boa/\"; the name at the\n-\t * remote is refs/foo/bar/ with the remaining part of the\n-\t * start_ref.  The length of the prefix on the RHS is (end -\n-\t * colon - 2), including the slash immediately before the\n-\t * asterisk.\n-\t */\n-\tif ((strlen(start_ref) < end - colon - 2) ||\n-\t    memcmp(start_ref, colon + 1, end - colon - 2))\n-\t\treturn 0; /* does not match prefix */\n-\n-\t/* Replace the asterisk with the remote branch name.  */\n-\tnfasprintf(&config_repo, \"%.*s%s\",\n-\t\t   (colon - 1) - value, value,\n-\t\t   start_ref + (end - colon - 2));\n-\treturn 1;\n-}\n-\n-static int get_remote_config(const char *key, const char *value)\n-{\n-\tconst char *var;\n-\tif (prefixcmp(key, \"remote.\"))\n-\t\treturn 0;\n-\n-\tvar = strrchr(key, '.');\n-\tif (var == key + 6 || strcmp(var, \".fetch\"))\n-\t\treturn 0;\n-\t/*\n-\t * Ok, we are looking at key == \"remote.$foo.fetch\";\n-\t */\n-\tif (get_remote_branch_name(value))\n-\t\tnfasprintf(&config_remote, \"%.*s\", var - (key + 7), key + 7);\n-\n-\treturn 0;\n-}\n-\n-static void set_branch_merge(const char *name, const char *config_remote,\n-\t\t\t     const char *config_repo)\n-{\n-\tchar key[1024];\n-\tif (sizeof(key) <=\n-\t    snprintf(key, sizeof(key), \"branch.%s.remote\", name))\n-\t\tdie(\"what a long branch name you have!\");\n-\tgit_config_set(key, config_remote);\n-\n-\t/*\n-\t * We do not have to check if we have enough space for\n-\t * the 'merge' key, since it's shorter than the\n-\t * previous 'remote' key, which we already checked.\n-\t */\n-\tsnprintf(key, sizeof(key), \"branch.%s.merge\", name);\n-\tgit_config_set(key, config_repo);\n-}\n-\n-static void set_branch_defaults(const char *name, const char *real_ref)\n-{\n-\t/*\n-\t * name is the name of new branch under refs/heads;\n-\t * real_ref is typically refs/remotes/$foo/$bar, where\n-\t * $foo is the remote name (there typically are no slashes)\n-\t * and $bar is the branch name we map from the remote\n-\t * (it could have slashes).\n-\t */\n-\tstart_ref = real_ref;\n-\tgit_config(get_remote_config);\n-\tif (!config_repo && !config_remote &&\n-\t    !prefixcmp(real_ref, \"refs/heads/\")) {\n-\t\tset_branch_merge(name, \".\", real_ref);\n-\t\tprintf(\"Branch %s set up to track local branch %s.\\n\",\n-\t\t       name, real_ref);\n-\t}\n-\n-\tif (config_repo && config_remote) {\n-\t\tset_branch_merge(name, config_remote, config_repo);\n-\t\tprintf(\"Branch %s set up to track remote branch %s.\\n\",\n-\t\t       name, real_ref);\n-\t}\n-\n-\tif (config_repo)\n-\t\tfree(config_repo);\n-\tif (config_remote)\n-\t\tfree(config_remote);\n+struct tracking {\n+\tstruct refspec spec;\n+\tchar *dst;\n+\tconst char *remote;\n+\tint matches;\n+};\n+\n+static int find_tracked_branch(struct remote *remote, void *priv)\n+{\n+\tstruct tracking *tracking = priv;\n+\n+\tif (!remote_find_tracking(remote, &tracking->spec, 1)) {\n+\t\tif (++tracking->matches == 1) {\n+\t\t\ttracking->dst = tracking->spec.dst;\n+\t\t\ttracking->remote = remote->name;\n+\t\t} else {\n+\t\t\tfree(tracking->spec.dst);\n+\t\t\tfree(tracking->dst);\n+\t\t\ttracking->dst = NULL;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+\n+/*\n+ * This is called when new_ref is branched off of orig_ref, and tries\n+ * to infer the settings for branch.<new_ref>.{remote,merge} from the\n+ * config.\n+ */\n+static int setup_tracking(const char *new_ref, const char *orig_ref)\n+{\n+\tchar key[1024];\n+\tstruct tracking tracking;\n+\n+\tif (strlen(new_ref) > 1024 - 7 - 7 - 1)\n+\t\treturn error(\"Tracking not set up: name too long: %s\",\n+\t\t\t\tnew_ref);\n+\n+\tmemset(&tracking, 0, sizeof(tracking));\n+\ttracking.spec.src = orig_ref;\n+\tif (for_each_remote(find_tracked_branch, &tracking) ||\n+\t\t\t!tracking.matches)\n+\t\treturn 1;\n+\n+\tif (tracking.matches > 1)\n+\t\treturn error(\"Not tracking: ambiguous information for ref %s\",\n+\t\t\t\torig_ref);\n+\n+\tif (tracking.matches == 1) {\n+\t\tsprintf(key, \"branch.%s.remote\", new_ref);\n+\t\tgit_config_set(key, tracking.remote ?  tracking.remote : \".\");\n+\t\tsprintf(key, \"branch.%s.merge\", new_ref);\n+\t\tgit_config_set(key, tracking.dst);\n+\t\tfree(tracking.dst);\n+\t\tprintf(\"Branch %s set up to track remote branch %s.\\n\",\n+\t\t\t       new_ref, orig_ref);\n+\t}\n+\n+\treturn 0;\n }\n \n static void create_branch(const char *name, const char *start_name,\n@@ -529,10 +468,8 @@ static void create_branch(const char *name, const char *start_name,\n \t/* When branching off a remote branch, set up so that git-pull\n \t   automatically merges from there.  So far, this is only done for\n \t   remotes registered via .git/config.  */\n-\tif (real_ref && (track == 2 ||\n-\t\t\t\t(track == 1 &&\n-\t\t\t\t !prefixcmp(real_ref, \"refs/remotes/\"))))\n-\t\tset_branch_defaults(name, real_ref);\n+\tif (real_ref && track)\n+\t\tsetup_tracking(name, real_ref);\n \n \tif (write_ref_sha1(lock, sha1, msg) < 0)\n \t\tdie(\"Failed to write ref: %s.\", strerror(errno));\n@@ -604,7 +541,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tbreak;\n \t\t}\n \t\tif (!strcmp(arg, \"--track\")) {\n-\t\t\ttrack = 2;\n+\t\t\ttrack = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--no-track\")) {\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex a19e961..ef1eeb7 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \\\n      test $(git config branch.my3.remote) = local &&\n      test $(git config branch.my3.merge) = refs/heads/master'\n \n-test_expect_success 'autosetupmerge = all' '\n+test_expect_success 'avoid ambiguous track' '\n \tgit config branch.autosetupmerge true &&\n+\tgit config remote.ambi1.url = lalala &&\n+\tgit config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&\n+\tgit config remote.ambi2.url = lilili &&\n+\tgit config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&\n \tgit branch all1 master &&\n-\ttest -z \"$(git config branch.all1.merge)\" &&\n-\tgit config branch.autosetupmerge all &&\n-\tgit branch all2 master &&\n-\ttest $(git config branch.all2.merge) = refs/heads/master\n+\ttest -z \"$(git config branch.all1.merge)\"\n '\n \n test_expect_success 'test overriding tracking setup via --no-track' \\\n@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \\\n      ! test \"$(git config branch.my2.remote)\" = local &&\n      ! test \"$(git config branch.my2.merge)\" = refs/heads/master'\n \n-test_expect_success 'test local tracking setup' \\\n+test_expect_success 'no tracking without .fetch entries' \\\n     'git branch --track my6 s &&\n-     test $(git config branch.my6.remote) = . &&\n-     test $(git config branch.my6.merge) = refs/heads/s'\n+     test -z \"$(git config branch.my6.remote)\" &&\n+     test -z \"$(git config branch.my6.merge)\"'\n \n test_expect_success 'test tracking setup via --track but deeper' \\\n     'git config remote.local.url . &&\n@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \\\n \n test_expect_success 'test deleting branch deletes branch config' \\\n     'git branch -d my7 &&\n-     test \"$(git config branch.my7.remote)\" = \"\" &&\n-     test \"$(git config branch.my7.merge)\" = \"\"'\n+     test -z \"$(git config branch.my7.remote)\" &&\n+     test -z \"$(git config branch.my7.merge)\"'\n \n test_expect_success 'test deleting branch without config' \\\n     'git branch my7 s &&\n-- \n1.5.3.rc0.2769.gd9be2\n"},{"id":"46909","messageId":"Pine.LNX.4.64.0707092323160.6977@iabervon.org","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707100401070.4131@racer.site","subject":"Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-07-10T03:55:53Z","receivedAt":"2007-07-10T03:55:53Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 10 Jul 2007, Johannes Schindelin wrote:\n\n> The function for_each_remote() does exactly what the name suggests.\n> \n> The function remote_find_tracking() was extended to be able to search\n> remote refs for a given local ref.  You have to set the parameter\n> \"reverse\" to true for that behavior.\n\nI think I'd like this better if reverse meant that it looked at \nrefspec->dst and set refspec->src, rather than returning the refspec \nreversed; the current version sets the refspec so that it's effectively \nsomething from the list, which makes it easier to understand.\n\nMaybe make it so the user calls it with at most one of src and dst NULL, \nand it returns with neither NULL or returns -1 if it can't find anything?\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"46914","messageId":"7vy7hosv7v.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707100401070.4131@racer.site","subject":"Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-10T05:07:00Z","receivedAt":"2007-07-10T05:07:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The function for_each_remote() does exactly what the name suggests.\n>\n> The function remote_find_tracking() was extended to be able to search\n> remote refs for a given local ref.  You have to set the parameter\n> \"reverse\" to true for that behavior.\n>\n> Both changes are required for the next step: simplification of\n> git-branch's --track functionality.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>\n> \tYou're right. I completely missed that functionality. Well, a\n> \tfew tweaks were needed. If this clashes too seriously with\n> \tDaniel's work, I will gladly redo it after his changes are\n> \tin \"next\".\n\nNo offence meant to Daniel, but I am inclined to postpone the\ncurrent round of changes from him to move the stuff further\nto get us closer to built-in git-fetch until 1.5.3 final is\ndone.  The amount of C code changes otherwise would be a bit too\nmuch for me to be comfortable between -rc0 and -rc1.\n\n> diff --git a/remote.c b/remote.c\n> index cf98a44..21adb0d 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -279,6 +279,26 @@ struct remote *remote_get(const char *name)\n>  \treturn ret;\n>  }\n>  \n> +int for_each_remote(each_remote_fn fn, void *priv)\n> +{\n> ...\n> +\t\tif ((result = fn(r, priv)))\n> +\t\t\tbreak;\n\nJust a minor style, but (you know what comes)...\n\n> @@ -289,34 +309,36 @@ int remote_has_uri(struct remote *remote, const char *uri)\n>  \treturn 0;\n>  }\n>  \n> +int remote_find_tracking(struct remote *remote, struct refspec *refspec,\n> +\t\tint reverse)\n>  {\n>  \tint i;\n>  \tfor (i = 0; i < remote->fetch_refspec_nr; i++) {\n>  \t\tstruct refspec *fetch = &remote->fetch[i];\n> +\t\tconst char *src = reverse ? fetch->dst : fetch->src;\n> +\t\tconst char *dst = reverse ? fetch->src : fetch->dst;\n\nI have to agree with Daniel here --- variable names src and dst\nare quite confusing.  It seems to mean that \"we search with\n'src' to fill 'dst', but if reverse incoming refspec is given\nreversed so matching refspec->src with what in fact is 'dst' in\nthe configuration file is fine\".  Utterly confusing.\n\nEven though this is a good opportunity for you to improve your\ncomment ratio in ohloh stats, I doubt any amount of explanation\ncan unconfuse readers of this code.\n\n>  \t\tif (!fetch->dst)\n>  \t\t\tcontinue;\n>\n>  \t\tif (fetch->pattern) {\n> +\t\t\tif (!prefixcmp(refspec->src, src)) {\n>  \t\t\t\trefspec->dst =\n> +\t\t\t\t\txmalloc(strlen(dst) +\n>  \t\t\t\t\t\tstrlen(refspec->src) -\n> +\t\t\t\t\t\tstrlen(src) + 1);\n> +\t\t\t\tstrcpy(refspec->dst, dst);\n> +\t\t\t\tstrcpy(refspec->dst + strlen(dst),\n> +\t\t\t\t       refspec->src + strlen(src));\n>  \t\t\t\trefspec->force = fetch->force;\n>  \t\t\t\treturn 0;\n>  \t\t\t}\n>  \t\t} else {\n> +\t\t\tif (!strcmp(refspec->src, src)) {\n> +\t\t\t\trefspec->dst = xstrdup(dst);\n>  \t\t\t\trefspec->force = fetch->force;\n>  \t\t\t\treturn 0;\n>  \t\t\t}\n>  \t\t}\n>  \t}\n> -\trefspec->dst = NULL;\n>  \treturn -1;\n>  }\n\nThe original remote_find_tracking() took a single remote and a\nrefspec with src filled, and returned the given refspec after\nfilling its dst, if an appropriate tracking was configured for\nthe remote.  What you want to do is from the same remote\ninformation and a refspec with dst filled to find a src branch\nthat would be stored to that dst.\n\nIn either case, incoming refspec would not have both src and dst\nfilled.  The caller has one side of the information and asking\nfor the other.  As Daniel suggests, the 'reverse' parameter is\nnot needed.  If you must have it, please do not call it\n'reverse' -- it is more like \"find_by_dst\".\n\nFollowing Daniel's suggestion might make the code a bit\nlengthier, but I think that would not be as confusing.\n\n> diff --git a/send-pack.c b/send-pack.c\n> index fecbda9..9fdd7b4 100644\n> --- a/send-pack.c\n> +++ b/send-pack.c\n> @@ -305,8 +305,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha\n>  \t\tif (remote) {\n>  \t\t\tstruct refspec rs;\n>  \t\t\trs.src = ref->name;\n> -\t\t\tremote_find_tracking(remote, &rs);\n> -\t\t\tif (rs.dst) {\n> +\t\t\tif (!remote_find_tracking(remote, &rs, 0)) {\n>  \t\t\t\tstruct ref_lock *lock;\n>  \t\t\t\tfprintf(stderr, \" Also local %s\\n\", rs.dst);\n>  \t\t\t\tif (will_delete_ref) {\n"},{"id":"46915","messageId":"Pine.LNX.4.64.0707100109290.6977@iabervon.org","threadId":"8906","inReplyTo":"7vy7hosv7v.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-07-10T05:23:37Z","receivedAt":"2007-07-10T05:23:37Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Mon, 9 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > \tYou're right. I completely missed that functionality. Well, a\n> > \tfew tweaks were needed. If this clashes too seriously with\n> > \tDaniel's work, I will gladly redo it after his changes are\n> > \tin \"next\".\n> \n> No offence meant to Daniel, but I am inclined to postpone the\n> current round of changes from him to move the stuff further\n> to get us closer to built-in git-fetch until 1.5.3 final is\n> done.  The amount of C code changes otherwise would be a bit too\n> much for me to be comfortable between -rc0 and -rc1.\n\nThat's what I'd expect; I'm posting stuff now so that I'm not proposing it \nunreviewed after 1.5.3. Certainly anything that's needed to fix current \nissues should go ahead of these changes, and it doesn't look like there \nwould be any conflicts anyway, aside from maybe adding two functions in \nthe same place in the file, which is trivial to fix by hand.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"46950","messageId":"Pine.LNX.4.64.0707101511440.4047@racer.site","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707092323160.6977@iabervon.org","subject":"Re: [PATCH 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T14:11:52Z","receivedAt":"2007-07-10T14:11:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 9 Jul 2007, Daniel Barkalow wrote:\n\n> On Tue, 10 Jul 2007, Johannes Schindelin wrote:\n> \n> > The function for_each_remote() does exactly what the name suggests.\n> > \n> > The function remote_find_tracking() was extended to be able to search\n> > remote refs for a given local ref.  You have to set the parameter\n> > \"reverse\" to true for that behavior.\n> \n> I think I'd like this better if reverse meant that it looked at \n> refspec->dst and set refspec->src, rather than returning the refspec \n> reversed; the current version sets the refspec so that it's effectively \n> something from the list, which makes it easier to understand.\n> \n> Maybe make it so the user calls it with at most one of src and dst NULL, \n> and it returns with neither NULL or returns -1 if it can't find anything?\n\nWill do.\n\nCiao,\nDscho\n"},{"id":"46972","messageId":"Pine.LNX.4.64.0707101848050.4047@racer.site","threadId":"8906","inReplyTo":"7vy7hosv7v.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T17:48:40Z","receivedAt":"2007-07-10T17:48:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe function for_each_remote() does exactly what the name suggests.\n\nThe function remote_find_tracking() was extended to be able to search\nremote refs for a given local ref.  You have to set the parameter\n\"reverse\" to true for that behavior.\n\nBoth changes are required for the next step: simplification of\ngit-branch's --track functionality.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tThanks, Daniel and Junio, for your suggestions.\n\n remote.c    |   60 ++++++++++++++++++++++++++++++++++++++++++++--------------\n remote.h    |    5 +++-\n send-pack.c |    4 +-\n 3 files changed, 51 insertions(+), 18 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 09c4279..bb774d0 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -279,6 +279,25 @@ struct remote *remote_get(const char *name)\n \treturn ret;\n }\n \n+int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\tint i, result = 0;\n+\tread_config();\n+\tfor (i = 0; i < allocated_remotes && !result; i++) {\n+\t\tstruct remote *r = remotes[i];\n+\t\tif (!r)\n+\t\t\tcontinue;\n+\t\tif (!r->fetch)\n+\t\t\tr->fetch = parse_ref_spec(r->fetch_refspec_nr,\n+\t\t\t\t\tr->fetch_refspec);\n+\t\tif (!r->push)\n+\t\t\tr->push = parse_ref_spec(r->push_refspec_nr,\n+\t\t\t\t\tr->push_refspec);\n+\t\tresult = fn(r, priv);\n+\t}\n+\treturn result;\n+}\n+\n int remote_has_uri(struct remote *remote, const char *uri)\n {\n \tint i;\n@@ -291,32 +310,43 @@ int remote_has_uri(struct remote *remote, const char *uri)\n \n int remote_find_tracking(struct remote *remote, struct refspec *refspec)\n {\n+\tint find_src = refspec->src == NULL;\n+\tchar *needle, **result;\n \tint i;\n+\n+\tif (find_src) {\n+\t\tif (refspec->dst == NULL)\n+\t\t\treturn error(\"find_tracking: need either src or dst\");\n+\t\tneedle = refspec->dst;\n+\t\tresult = &refspec->src;\n+\t} else {\n+\t\tneedle = refspec->src;\n+\t\tresult = &refspec->dst;\n+\t}\n+\n \tfor (i = 0; i < remote->fetch_refspec_nr; i++) {\n \t\tstruct refspec *fetch = &remote->fetch[i];\n+\t\tconst char *key = find_src ? fetch->dst : fetch->src;\n+\t\tconst char *value = find_src ? fetch->src : fetch->dst;\n \t\tif (!fetch->dst)\n \t\t\tcontinue;\n \t\tif (fetch->pattern) {\n-\t\t\tif (!prefixcmp(refspec->src, fetch->src)) {\n-\t\t\t\trefspec->dst =\n-\t\t\t\t\txmalloc(strlen(fetch->dst) +\n-\t\t\t\t\t\tstrlen(refspec->src) -\n-\t\t\t\t\t\tstrlen(fetch->src) + 1);\n-\t\t\t\tstrcpy(refspec->dst, fetch->dst);\n-\t\t\t\tstrcpy(refspec->dst + strlen(fetch->dst),\n-\t\t\t\t       refspec->src + strlen(fetch->src));\n-\t\t\t\trefspec->force = fetch->force;\n-\t\t\t\treturn 0;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tif (!strcmp(refspec->src, fetch->src)) {\n-\t\t\t\trefspec->dst = xstrdup(fetch->dst);\n+\t\t\tif (!prefixcmp(needle, key)) {\n+\t\t\t\t*result = xmalloc(strlen(value) +\n+\t\t\t\t\t\t  strlen(needle) -\n+\t\t\t\t\t\t  strlen(key) + 1);\n+\t\t\t\tstrcpy(*result, value);\n+\t\t\t\tstrcpy(*result + strlen(value),\n+\t\t\t\t       needle + strlen(key));\n \t\t\t\trefspec->force = fetch->force;\n \t\t\t\treturn 0;\n \t\t\t}\n+\t\t} else if (!strcmp(needle, key)) {\n+\t\t\t*result = xstrdup(value);\n+\t\t\trefspec->force = fetch->force;\n+\t\t\treturn 0;\n \t\t}\n \t}\n-\trefspec->dst = NULL;\n \treturn -1;\n }\n \ndiff --git a/remote.h b/remote.h\nindex 080b7da..17b8b5b 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -20,13 +20,16 @@ struct remote {\n \n struct remote *remote_get(const char *name);\n \n+typedef int each_remote_fn(struct remote *remote, void *priv);\n+int for_each_remote(each_remote_fn fn, void *priv);\n+\n int remote_has_uri(struct remote *remote, const char *uri);\n \n struct refspec {\n \tunsigned force : 1;\n \tunsigned pattern : 1;\n \n-\tconst char *src;\n+\tchar *src;\n \tchar *dst;\n };\n \ndiff --git a/send-pack.c b/send-pack.c\nindex fecbda9..9fc8a81 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -305,8 +305,8 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha\n \t\tif (remote) {\n \t\t\tstruct refspec rs;\n \t\t\trs.src = ref->name;\n-\t\t\tremote_find_tracking(remote, &rs);\n-\t\t\tif (rs.dst) {\n+\t\t\trs.dst = NULL;\n+\t\t\tif (!remote_find_tracking(remote, &rs)) {\n \t\t\t\tstruct ref_lock *lock;\n \t\t\t\tfprintf(stderr, \" Also local %s\\n\", rs.dst);\n \t\t\t\tif (will_delete_ref) {\n-- \n1.5.3.rc0.2783.gf3f7\n"},{"id":"46973","messageId":"Pine.LNX.4.64.0707101850140.4047@racer.site","threadId":"8906","inReplyTo":"7vy7hosv7v.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH v2 2/2] branch --track: code cleanup and saner handling of local branches","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T17:50:44Z","receivedAt":"2007-07-10T17:50:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch cleans up some complicated code, and replaces it with a\ncleaner version, using code from remote.[ch], which got extended a\nlittle in the process.  This also enables us to fix two cases:\n\nThe earlier \"fix\" to setup tracking only when the original ref started \nwith \"refs/remotes\" is wrong.  You are absolutely allowed to use a \nseparate layout for your tracking branches.  The correct fix, of course, \nis to set up tracking information only when there is a matching \nremote.<nick>.fetch line containing a colon.\n\nAnother corner case was not handled properly.  If two remotes write to\nthe original ref, just warn the user and do not set up tracking.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tSorry, but I really do not feel like manually making the patch\n\tnicer to read today.  It is really annoying to me to see the\n\tempty and the not-really-interesting lines match up.  Oh, well.\n\n builtin-branch.c  |  170 +++++++++++++++++------------------------------------\n t/t3200-branch.sh |   21 ++++---\n 2 files changed, 66 insertions(+), 125 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 49195a1..d16d3f2 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -10,6 +10,7 @@\n #include \"refs.h\"\n #include \"commit.h\"\n #include \"builtin.h\"\n+#include \"remote.h\"\n \n static const char builtin_branch_usage[] =\n   \"git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] [--sort-by-date]\";\n@@ -22,7 +23,7 @@ static const char builtin_branch_usage[] =\n static const char *head;\n static unsigned char head_sha1[20];\n \n-static int branch_track = 1; /* 0 = none, 1 = remotes, 2 = all */\n+static int branch_track = 1;\n \n static int branch_use_color;\n static char branch_colors[][COLOR_MAXLEN] = {\n@@ -66,12 +67,8 @@ static int git_branch_config(const char *var, const char *value)\n \t\tcolor_parse(value, var, branch_colors[slot]);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(var, \"branch.autosetupmerge\")) {\n-\t\tif (!strcmp(value, \"all\"))\n-\t\t\tbranch_track = 2;\n-\t\telse\n+\tif (!strcmp(var, \"branch.autosetupmerge\"))\n \t\t\tbranch_track = git_config_bool(var, value);\n-\t}\n \n \treturn git_default_config(var, value);\n }\n@@ -349,125 +346,70 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev,\n \tfree_ref_list(&ref_list);\n }\n \n-static char *config_repo;\n-static char *config_remote;\n-static const char *start_ref;\n+struct tracking {\n+\tstruct refspec spec;\n+\tchar *src;\n+\tconst char *remote;\n+\tint matches;\n+};\n \n-static int get_remote_branch_name(const char *value)\n+static int find_tracked_branch(struct remote *remote, void *priv)\n {\n-\tconst char *colon;\n-\tconst char *end;\n+\tstruct tracking *tracking = priv;\n \n-\tif (*value == '+')\n-\t\tvalue++;\n-\n-\tcolon = strchr(value, ':');\n-\tif (!colon)\n-\t\treturn 0;\n-\n-\tend = value + strlen(value);\n-\n-\t/*\n-\t * Try an exact match first.  I.e. handle the case where the\n-\t * value is \"$anything:refs/foo/bar/baz\" and start_ref is exactly\n-\t * \"refs/foo/bar/baz\". Then the name at the remote is $anything.\n-\t */\n-\tif (!strcmp(colon + 1, start_ref)) {\n-\t\t/* Truncate the value before the colon. */\n-\t\tnfasprintf(&config_repo, \"%.*s\", colon - value, value);\n-\t\treturn 1;\n+\tif (!remote_find_tracking(remote, &tracking->spec)) {\n+\t\tif (++tracking->matches == 1) {\n+\t\t\ttracking->src = tracking->spec.src;\n+\t\t\ttracking->remote = remote->name;\n+\t\t} else {\n+\t\t\tfree(tracking->spec.src);\n+\t\t\tif (tracking->src) {\n+\t\t\t\tfree(tracking->src);\n+\t\t\t\ttracking->src = NULL;\n+\t\t\t}\n+\t\t}\n+\t\ttracking->spec.src = NULL;\n \t}\n \n-\t/*\n-\t * Is this a wildcard match?\n-\t */\n-\tif ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||\n-\t    (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')\n-\t\treturn 0;\n-\n-\t/*\n-\t * Value is \"refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>\"\n-\t * and start_ref begins with \"refs/baz/boa/\"; the name at the\n-\t * remote is refs/foo/bar/ with the remaining part of the\n-\t * start_ref.  The length of the prefix on the RHS is (end -\n-\t * colon - 2), including the slash immediately before the\n-\t * asterisk.\n-\t */\n-\tif ((strlen(start_ref) < end - colon - 2) ||\n-\t    memcmp(start_ref, colon + 1, end - colon - 2))\n-\t\treturn 0; /* does not match prefix */\n-\n-\t/* Replace the asterisk with the remote branch name.  */\n-\tnfasprintf(&config_repo, \"%.*s%s\",\n-\t\t   (colon - 1) - value, value,\n-\t\t   start_ref + (end - colon - 2));\n-\treturn 1;\n-}\n-\n-static int get_remote_config(const char *key, const char *value)\n-{\n-\tconst char *var;\n-\tif (prefixcmp(key, \"remote.\"))\n-\t\treturn 0;\n-\n-\tvar = strrchr(key, '.');\n-\tif (var == key + 6 || strcmp(var, \".fetch\"))\n-\t\treturn 0;\n-\t/*\n-\t * Ok, we are looking at key == \"remote.$foo.fetch\";\n-\t */\n-\tif (get_remote_branch_name(value))\n-\t\tnfasprintf(&config_remote, \"%.*s\", var - (key + 7), key + 7);\n-\n \treturn 0;\n }\n \n-static void set_branch_merge(const char *name, const char *config_remote,\n-\t\t\t     const char *config_repo)\n+\n+/*\n+ * This is called when new_ref is branched off of orig_ref, and tries\n+ * to infer the settings for branch.<new_ref>.{remote,merge} from the\n+ * config.\n+ */\n+static int setup_tracking(const char *new_ref, const char *orig_ref)\n {\n \tchar key[1024];\n-\tif (sizeof(key) <=\n-\t    snprintf(key, sizeof(key), \"branch.%s.remote\", name))\n-\t\tdie(\"what a long branch name you have!\");\n-\tgit_config_set(key, config_remote);\n-\n-\t/*\n-\t * We do not have to check if we have enough space for\n-\t * the 'merge' key, since it's shorter than the\n-\t * previous 'remote' key, which we already checked.\n-\t */\n-\tsnprintf(key, sizeof(key), \"branch.%s.merge\", name);\n-\tgit_config_set(key, config_repo);\n-}\n+\tstruct tracking tracking;\n \n-static void set_branch_defaults(const char *name, const char *real_ref)\n-{\n-\t/*\n-\t * name is the name of new branch under refs/heads;\n-\t * real_ref is typically refs/remotes/$foo/$bar, where\n-\t * $foo is the remote name (there typically are no slashes)\n-\t * and $bar is the branch name we map from the remote\n-\t * (it could have slashes).\n-\t */\n-\tstart_ref = real_ref;\n-\tgit_config(get_remote_config);\n-\tif (!config_repo && !config_remote &&\n-\t    !prefixcmp(real_ref, \"refs/heads/\")) {\n-\t\tset_branch_merge(name, \".\", real_ref);\n-\t\tprintf(\"Branch %s set up to track local branch %s.\\n\",\n-\t\t       name, real_ref);\n-\t}\n+\tif (strlen(new_ref) > 1024 - 7 - 7 - 1)\n+\t\treturn error(\"Tracking not set up: name too long: %s\",\n+\t\t\t\tnew_ref);\n \n-\tif (config_repo && config_remote) {\n-\t\tset_branch_merge(name, config_remote, config_repo);\n+\tmemset(&tracking, 0, sizeof(tracking));\n+\ttracking.spec.dst = (char *)orig_ref;\n+\tif (for_each_remote(find_tracked_branch, &tracking) ||\n+\t\t\t!tracking.matches)\n+\t\treturn 1;\n+\n+\tif (tracking.matches > 1)\n+\t\treturn error(\"Not tracking: ambiguous information for ref %s\",\n+\t\t\t\torig_ref);\n+\n+\tif (tracking.matches == 1) {\n+\t\tsprintf(key, \"branch.%s.remote\", new_ref);\n+\t\tgit_config_set(key, tracking.remote ?  tracking.remote : \".\");\n+\t\tsprintf(key, \"branch.%s.merge\", new_ref);\n+\t\tgit_config_set(key, tracking.src);\n+\t\tfree(tracking.src);\n \t\tprintf(\"Branch %s set up to track remote branch %s.\\n\",\n-\t\t       name, real_ref);\n+\t\t\t       new_ref, orig_ref);\n \t}\n \n-\tif (config_repo)\n-\t\tfree(config_repo);\n-\tif (config_remote)\n-\t\tfree(config_remote);\n+\treturn 0;\n }\n \n static void create_branch(const char *name, const char *start_name,\n@@ -529,10 +471,8 @@ static void create_branch(const char *name, const char *start_name,\n \t/* When branching off a remote branch, set up so that git-pull\n \t   automatically merges from there.  So far, this is only done for\n \t   remotes registered via .git/config.  */\n-\tif (real_ref && (track == 2 ||\n-\t\t\t\t(track == 1 &&\n-\t\t\t\t !prefixcmp(real_ref, \"refs/remotes/\"))))\n-\t\tset_branch_defaults(name, real_ref);\n+\tif (real_ref && track)\n+\t\tsetup_tracking(name, real_ref);\n \n \tif (write_ref_sha1(lock, sha1, msg) < 0)\n \t\tdie(\"Failed to write ref: %s.\", strerror(errno));\n@@ -604,7 +544,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tbreak;\n \t\t}\n \t\tif (!strcmp(arg, \"--track\")) {\n-\t\t\ttrack = 2;\n+\t\t\ttrack = 1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--no-track\")) {\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex a19e961..ef1eeb7 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -148,13 +148,14 @@ test_expect_success 'test tracking setup via config' \\\n      test $(git config branch.my3.remote) = local &&\n      test $(git config branch.my3.merge) = refs/heads/master'\n \n-test_expect_success 'autosetupmerge = all' '\n+test_expect_success 'avoid ambiguous track' '\n \tgit config branch.autosetupmerge true &&\n+\tgit config remote.ambi1.url = lalala &&\n+\tgit config remote.ambi1.fetch = refs/heads/lalala:refs/heads/master &&\n+\tgit config remote.ambi2.url = lilili &&\n+\tgit config remote.ambi2.fetch = refs/heads/lilili:refs/heads/master &&\n \tgit branch all1 master &&\n-\ttest -z \"$(git config branch.all1.merge)\" &&\n-\tgit config branch.autosetupmerge all &&\n-\tgit branch all2 master &&\n-\ttest $(git config branch.all2.merge) = refs/heads/master\n+\ttest -z \"$(git config branch.all1.merge)\"\n '\n \n test_expect_success 'test overriding tracking setup via --no-track' \\\n@@ -167,10 +168,10 @@ test_expect_success 'test overriding tracking setup via --no-track' \\\n      ! test \"$(git config branch.my2.remote)\" = local &&\n      ! test \"$(git config branch.my2.merge)\" = refs/heads/master'\n \n-test_expect_success 'test local tracking setup' \\\n+test_expect_success 'no tracking without .fetch entries' \\\n     'git branch --track my6 s &&\n-     test $(git config branch.my6.remote) = . &&\n-     test $(git config branch.my6.merge) = refs/heads/s'\n+     test -z \"$(git config branch.my6.remote)\" &&\n+     test -z \"$(git config branch.my6.merge)\"'\n \n test_expect_success 'test tracking setup via --track but deeper' \\\n     'git config remote.local.url . &&\n@@ -182,8 +183,8 @@ test_expect_success 'test tracking setup via --track but deeper' \\\n \n test_expect_success 'test deleting branch deletes branch config' \\\n     'git branch -d my7 &&\n-     test \"$(git config branch.my7.remote)\" = \"\" &&\n-     test \"$(git config branch.my7.merge)\" = \"\"'\n+     test -z \"$(git config branch.my7.remote)\" &&\n+     test -z \"$(git config branch.my7.merge)\"'\n \n test_expect_success 'test deleting branch without config' \\\n     'git branch my7 s &&\n-- \n1.5.3.rc0.2783.gf3f7\n"},{"id":"46978","messageId":"7vtzscqf2r.fsf@assigned-by-dhcp.cox.net","threadId":"8906","inReplyTo":"Pine.LNX.4.64.0707101848050.4047@racer.site","subject":"Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-07-10T18:38:36Z","receivedAt":"2007-07-10T18:38:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The function for_each_remote() does exactly what the name suggests.\n>\n> The function remote_find_tracking() was extended to be able to search\n> remote refs for a given local ref.  You have to set the parameter\n> \"reverse\" to true for that behavior.\n\nThe updated patch does not use \"reverse\" but the old description\nis still there.\n\nDaniel, one thing I fear about your \"I want to store the message\nin the object store so that I can reuse even after I re-polish\nthe series\" desire on the cover letter topic is this kind of\ngotcha, and that is why I suggested \"*** BLURB GOES HERE ***\".\nBoth the summary (diffstat and shortlog) part and the\ndescription part should be kept fresh in the updated 0/N; while\nwe can automate the summary part whenever we re-generate 0/N,\nyou cannot automate the description part.\n"},{"id":"46980","messageId":"Pine.LNX.4.64.0707102026010.4047@racer.site","threadId":"8906","inReplyTo":"7vtzscqf2r.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-10T19:28:58Z","receivedAt":"2007-07-10T19:28:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 10 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > The function for_each_remote() does exactly what the name suggests.\n> >\n> > The function remote_find_tracking() was extended to be able to search\n> > remote refs for a given local ref.  You have to set the parameter\n> > \"reverse\" to true for that behavior.\n> \n> The updated patch does not use \"reverse\" but the old description\n> is still there.\n\nUrgh. Right. May I ask you to paste this instead?\n\nThe function remote_find_tracking() was extended to be able to search \nremote refs for a given local ref.  You have to set either src or dst in \nthe refspec, and remote_find_tracking() will fill in the other and return \n0.\n\n> Daniel, one thing I fear about your \"I want to store the message\n> in the object store so that I can reuse even after I re-polish\n> the series\" desire on the cover letter topic is this kind of\n> gotcha, and that is why I suggested \"*** BLURB GOES HERE ***\".\n\nI am happy that my fsckup served a purpose, then.  (And maybe this would \nbe a good hint in rebase -i's man page, too, since that is how the error \nwas introduced here.)\n\nCiao,\nDscho\n"},{"id":"46984","messageId":"Pine.LNX.4.64.0707101503470.6977@iabervon.org","threadId":"8906","inReplyTo":"7vtzscqf2r.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH v2 1/2] Add for_each_remote() function, and extend remote_find_tracking()","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-07-10T21:09:45Z","receivedAt":"2007-07-10T21:09:45Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 10 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > The function for_each_remote() does exactly what the name suggests.\n> >\n> > The function remote_find_tracking() was extended to be able to search\n> > remote refs for a given local ref.  You have to set the parameter\n> > \"reverse\" to true for that behavior.\n> \n> The updated patch does not use \"reverse\" but the old description\n> is still there.\n> \n> Daniel, one thing I fear about your \"I want to store the message\n> in the object store so that I can reuse even after I re-polish\n> the series\" desire on the cover letter topic is this kind of\n> gotcha, and that is why I suggested \"*** BLURB GOES HERE ***\".\n> Both the summary (diffstat and shortlog) part and the\n> description part should be kept fresh in the updated 0/N; while\n> we can automate the summary part whenever we re-generate 0/N,\n> you cannot automate the description part.\n\nIt seems to me that commit messages are much more likely to mention the \nsorts of details that are affected by review than cover letters are. \nFurthermore, if the message is coming out of a tag on the head of the \nseries, whatever is used to put the tag onto the new head of the series \nwould present the buffer for editting again, just like commit --amend \ndoes. So the user would be just as likely to think to update a series \nheader as a commit message, and less likely to need to.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"}]}