{"thread":{"id":"8437","subject":"git branch --track and remote branches with / in the name","startedAt":"2007-06-05T09:04:04Z","lastAt":"2007-06-05T11:35:57Z","messageCount":3,"participants":["Gerrit Pape","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"44046","messageId":"20070605090404.24707.qmail@b58755fd627261.315fe32.mid.smarden.org","threadId":"8437","inReplyTo":null,"subject":"git branch --track and remote branches with / in the name","fromName":"Gerrit Pape","fromEmail":"pape@smarden.org","sentAt":"2007-06-05T09:04:04Z","receivedAt":"2007-06-05T09:04:04Z","isPatch":false,"sender":{"key":"pape@smarden.org","avatar":"https://avatars.githubusercontent.com/u/143170252?v=4"},"body":"Hi, the --track option to git branch or git checkout doesn't work for\nremote branches that contain a slash in the name:\n\n $ tail -n6 .git/config\n [remote \"origin\"]\n         url = /tmp/repo\n         fetch = +refs/heads/*:refs/remotes/origin/*\n [branch \"master\"]\n         remote = origin\n         merge = refs/heads/master\n $ git branch -r\n   origin/HEAD\n   origin/b\n   origin/foo/b\n   origin/master\n $ git branch --track b origin/b    \n Branch b set up to track remote branch refs/remotes/origin/b.\n $ git branch --track foo/b origin/foo/b\n $ tail -n6 .git/config\n [branch \"master\"]\n         remote = origin\n         merge = refs/heads/master\n [branch \"b\"]\n         remote = origin\n         merge = refs/heads/b\n $ \n\nRegards, Gerrit. \n"},{"id":"44057","messageId":"7v4plmd7kg.fsf@assigned-by-dhcp.cox.net","threadId":"8437","inReplyTo":"20070605090404.24707.qmail@b58755fd627261.315fe32.mid.smarden.org","subject":"Re: git branch --track and remote branches with / in the name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-06-05T10:22:39Z","receivedAt":"2007-06-05T10:22:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Gerrit Pape <pape@smarden.org> writes:\n\n> Hi, the --track option to git branch or git checkout doesn't work for\n> remote branches that contain a slash in the name:\n>\n>  $ tail -n6 .git/config\n>  [remote \"origin\"]\n>          url = /tmp/repo\n>          fetch = +refs/heads/*:refs/remotes/origin/*\n>  [branch \"master\"]\n>          remote = origin\n>          merge = refs/heads/master\n>  $ git branch -r\n>    origin/HEAD\n>    origin/b\n>    origin/foo/b\n>    origin/master\n>  $ git branch --track b origin/b    \n>  Branch b set up to track remote branch refs/remotes/origin/b.\n>  $ git branch --track foo/b origin/foo/b\n>  $ tail -n6 .git/config\n>  [branch \"master\"]\n>          remote = origin\n>          merge = refs/heads/master\n>  [branch \"b\"]\n>          remote = origin\n>          merge = refs/heads/b\n>  $ \n\nIndeed the \"find matching remote tracking branch\" code was\nsomewhat sloppy and needlessly complex/clever.\n\nHow about this patch?\n\n-- >8 --\ngit-branch --track: fix tracking branch computation.\n\nThe original code did not take hierarchical branch names into account at all.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-branch.c  |   67 ++++++++++++++++++++++++++++++++--------------------\n t/t3200-branch.sh |   16 +++++++++---\n 2 files changed, 53 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex a5b6bbe..4bed12b 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -317,8 +317,6 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)\n static char *config_repo;\n static char *config_remote;\n static const char *start_ref;\n-static int start_len;\n-static int base_len;\n \n static int get_remote_branch_name(const char *value)\n {\n@@ -334,26 +332,41 @@ static int get_remote_branch_name(const char *value)\n \n \tend = value + strlen(value);\n \n-\t/* Try an exact match first.  */\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\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/* Try with a wildcard match now.  */\n-\tif (end - value > 2 && end[-2] == '/' && end[-1] == '*' &&\n-\t    colon - value > 2 && colon[-2] == '/' && colon[-1] == '*' &&\n-\t    (end - 2) - (colon + 1) == base_len &&\n-\t    !strncmp(colon + 1, start_ref, base_len)) {\n-\t\t/* Replace the star with the remote branch name.  */\n-\t\tnfasprintf(&config_repo, \"%.*s%s\",\n-\t\t\t   (colon - 2) - value, value,\n-\t\t\t   start_ref + base_len);\n-\t\treturn 1;\n-\t}\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-\treturn 0;\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@@ -363,10 +376,12 @@ static int get_remote_config(const char *key, const char *value)\n \t\treturn 0;\n \n \tvar = strrchr(key, '.');\n-\tif (var == key + 6)\n+\tif (var == key + 6 || strcmp(var, \".fetch\"))\n \t\treturn 0;\n-\n-\tif (!strcmp(var, \".fetch\") && get_remote_branch_name(value))\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@@ -392,14 +407,14 @@ static void set_branch_merge(const char *name, const char *config_remote,\n \n static void set_branch_defaults(const char *name, const char *real_ref)\n {\n-\tconst char *slash = strrchr(real_ref, '/');\n-\n-\tif (!slash)\n-\t\treturn;\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-\tstart_len = strlen(real_ref);\n-\tbase_len = slash - real_ref;\n \tgit_config(get_remote_config);\n \tif (!config_repo && !config_remote &&\n \t    !prefixcmp(real_ref, \"refs/heads/\")) {\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 828d553..6f6d884 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -136,8 +136,8 @@ test_expect_success 'test tracking setup (non-wildcard, not matching)' \\\n      git-config remote.local.fetch refs/heads/s:refs/remotes/local/s &&\n      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&\n      git-branch --track my5 local/master &&\n-     ! test $(git-config branch.my5.remote) = local &&\n-     ! test $(git-config branch.my5.merge) = refs/heads/master'\n+     ! test \"$(git-config branch.my5.remote)\" = local &&\n+     ! test \"$(git-config branch.my5.merge)\" = refs/heads/master'\n \n test_expect_success 'test tracking setup via config' \\\n     'git-config branch.autosetupmerge true &&\n@@ -155,14 +155,22 @@ test_expect_success 'test overriding tracking setup via --no-track' \\\n      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&\n      git-branch --no-track my2 local/master &&\n      git-config branch.autosetupmerge false &&\n-     ! test $(git-config branch.my2.remote) = local &&\n-     ! test $(git-config branch.my2.merge) = refs/heads/master'\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     'git branch --track my6 s &&\n      test $(git-config branch.my6.remote) = . &&\n      test $(git-config branch.my6.merge) = refs/heads/s'\n \n+test_expect_success 'test tracking setup via --track but deeper' \\\n+    'git-config remote.local.url . &&\n+     git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+     (git-show-ref -q refs/remotes/local/o/o || git-fetch local) &&\n+     git-branch --track my7 local/o/o &&\n+     test \"$(git-config branch.my7.remote)\" = local &&\n+     test \"$(git-config branch.my7.merge)\" = refs/heads/o/o'\n+\n # Keep this test last, as it changes the current branch\n cat >expect <<EOF\n 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000\tbranch: Created from master\n-- \n1.5.2.1.116.g9f308\n"},{"id":"44069","messageId":"20070605113557.5803.qmail@81f254b7954e14.315fe32.mid.smarden.org","threadId":"8437","inReplyTo":"7v4plmd7kg.fsf@assigned-by-dhcp.cox.net","subject":"Re: git branch --track and remote branches with / in the name","fromName":"Gerrit Pape","fromEmail":"pape@smarden.org","sentAt":"2007-06-05T11:35:57Z","receivedAt":"2007-06-05T11:35:57Z","isPatch":false,"sender":{"key":"pape@smarden.org","avatar":"https://avatars.githubusercontent.com/u/143170252?v=4"},"body":"On Tue, Jun 05, 2007 at 03:22:39AM -0700, Junio C Hamano wrote:\n> Gerrit Pape <pape@smarden.org> writes:\n> > Hi, the --track option to git branch or git checkout doesn't work for\n> > remote branches that contain a slash in the name:\n\n> Indeed the \"find matching remote tracking branch\" code was\n> somewhat sloppy and needlessly complex/clever.\n> \n> How about this patch?\n\nWorks fine for me, including the selftest.  Thanks, Gerrit.\n"}]}