{"thread":{"id":"31526","subject":"is this behaviour expected for \"git clone --single-branch\"?","startedAt":"2012-09-13T18:38:28Z","lastAt":"2012-09-20T21:17:03Z","messageCount":39,"participants":["Ralf Thielow","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"198952","messageId":"CAN0XMOJsVOCzp2_BOQVUKy3cV8JA9AG+=Bw4tC2ZzDfK0AcQVg@mail.gmail.com","threadId":"31526","inReplyTo":null,"subject":"is this behaviour expected for \"git clone --single-branch\"?","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-13T18:38:28Z","receivedAt":"2012-09-13T18:38:28Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"Hi,\n\nI know people which have a separate directory for every\nbranch. In this case it doesn't make sense to download\nthe whole repo with all branches. So I guess the\n\"--single-branch\" option is the solution in that case!?!\nBut I'm wondering about it's behaviour.\n\n# first clone the branch of the repo\n$git clone --single-branch --branch master myrepo ./master\n$cd master\n\n# now calling \"git branch -a\" to see what I have\n$git branch -a\n*master\nremotes/origin/HEAD -> origin/master\nremotes/origin/master\n\n# fine, now pulling from origin\n$git pull\nFrom myrepo\n* [new branch]  foo  -> origin/foo\n* [new branch] bar -> origin/bar\n...\n\nHm?\n\n# looking again to my branches\n$git branch -a\n*master\nremotes/origin/HEAD -> origin/master\nremotes/origin/master\nremotes/origin/bar\nremotes/origin/foo\n...\n\nAfter cloning (or fetching), I now have all branches which is not\nwhat I want, because I'm only interested in the one I've cloned.\nI think it's not very useful for the use-case of having one directory\nfor one branch.\n\n$git version\ngit version 1.7.12.395.g6b149ce\n\nThanks\n"},{"id":"198956","messageId":"7v627h92xv.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CAN0XMOJsVOCzp2_BOQVUKy3cV8JA9AG+=Bw4tC2ZzDfK0AcQVg@mail.gmail.com","subject":"Re: is this behaviour expected for \"git clone --single-branch\"?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-13T18:45:48Z","receivedAt":"2012-09-13T18:45:48Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> # looking again to my branches\n\nDon't look at your branches, but look at how the refspecs are\nconfigured by \"git clone\" in .git/config; I suspect we just write\nthe default 'refs/heads/*:refs/remotes/origin/*' pattern.\n"},{"id":"198957","messageId":"CAN0XMOKszADpeaCG7VhL-AZ3m7_hSWV3NhEmPuH6FETzs=eDNg@mail.gmail.com","threadId":"31526","inReplyTo":"7v627h92xv.fsf@alter.siamese.dyndns.org","subject":"Re: is this behaviour expected for \"git clone --single-branch\"?","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-13T18:48:19Z","receivedAt":"2012-09-13T18:48:19Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Thu, Sep 13, 2012 at 8:45 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Ralf Thielow <ralf.thielow@gmail.com> writes:\n>\n>> # looking again to my branches\n>\n> Don't look at your branches, but look at how the refspecs are\n> configured by \"git clone\" in .git/config; I suspect we just write\n> the default 'refs/heads/*:refs/remotes/origin/*' pattern.\n\nIndeed.\n\n$ cat .git/config\n[remote \"origin\"]\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n"},{"id":"198996","messageId":"1347599357-15533-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"CAN0XMOKszADpeaCG7VhL-AZ3m7_hSWV3NhEmPuH6FETzs=eDNg@mail.gmail.com","subject":"[PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-14T05:09:17Z","receivedAt":"2012-09-14T05:09:17Z","isPatch":true,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After using \"git clone\" with the \"--single-branch\"\noption, the configured refspec for this repo was\n\"+refs/heads/*:refs/remotes/origin/*\".\nAfter fetching changes from this repo again, it'll\nreceive all refs instead of the single ref which\nwas used in \"--single-branch\". Fixing the refspec\nthat it just contains the ref of the branch which\nwas cloned.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n builtin/clone.c | 5 ++++-\n 1 Datei geändert, 4 Zeilen hinzugefügt(+), 1 Zeile entfernt(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..3e74d55 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -754,7 +754,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&branch_top, \"refs/remotes/%s/\", option_origin);\n \t}\n \n-\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n+\tif (option_single_branch)\n+\t\tstrbuf_addf(&value, \"+%s%s:%s%s\", src_ref_prefix, option_branch, branch_top.buf, option_branch);\n+\telse\n+\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n \n \tif (option_mirror || !option_bare) {\n \t\t/* Configure the remote */\n-- \n1.7.12.395.g6b149ce.dirty\n"},{"id":"198998","messageId":"7vr4q56uae.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347599357-15533-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T05:35:37Z","receivedAt":"2012-09-14T05:35:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> After using \"git clone\" with the \"--single-branch\"\n> option, the configured refspec for this repo was\n> \"+refs/heads/*:refs/remotes/origin/*\".\n> After fetching changes from this repo again, it'll\n> receive all refs instead of the single ref which\n> was used in \"--single-branch\". Fixing the refspec\n> that it just contains the ref of the branch which\n> was cloned.\n>\n> Signed-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n> ---\n>  builtin/clone.c | 5 ++++-\n>  1 Datei geändert, 4 Zeilen hinzugefügt(+), 1 Zeile entfernt(-)\n>\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 5e8f3ba..3e74d55 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -754,7 +754,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\tstrbuf_addf(&branch_top, \"refs/remotes/%s/\", option_origin);\n>  \t}\n>  \n> -\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n> +\tif (option_single_branch)\n> +\t\tstrbuf_addf(&value, \"+%s%s:%s%s\", src_ref_prefix, option_branch, branch_top.buf, option_branch);\n> +\telse\n> +\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n\nWho guarantees at this point in the codepath that option_branch is\nset when option_single_branch is non-zero?  Until we talk with the\nremote, \"clone --single-branch\" without an explicit \"--branch\" will\nnot learn which branch at the remote we are going to fetch (it will\nbe their HEAD).\n\nI wonder if this should be more like this:\n\n\tif (option_single_branch) {\n\t\tif (option_branch)\n\t\t\tYour patch \"+refs/heads/foo:refs/remotes/origin/foo\";\n\t\telse\n\t\t\t\"HEAD\";\n        } else {\n        \tOriginal \"+refs/heads/*:refs/remotes/origin/*\";\n\t}\n\nThat is, \"clone --single-branch\" will continue fetching from and\nintegrating with their HEAD without storing any remote tracking\nbranch.\n"},{"id":"199001","messageId":"7vipbh6qxa.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"7vr4q56uae.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T06:48:17Z","receivedAt":"2012-09-14T06:48:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Who guarantees at this point in the codepath that option_branch is\n> set when option_single_branch is non-zero?  Until we talk with the\n> remote, \"clone --single-branch\" without an explicit \"--branch\" will\n> not learn which branch at the remote we are going to fetch (it will\n> be their HEAD).\n>\n> I wonder if this should be more like this:\n>\n> \tif (option_single_branch) {\n> \t\tif (option_branch)\n> \t\t\tYour patch \"+refs/heads/foo:refs/remotes/origin/foo\";\n> \t\telse\n> \t\t\t\"HEAD\";\n>         } else {\n>         \tOriginal \"+refs/heads/*:refs/remotes/origin/*\";\n> \t}\n>\n> That is, \"clone --single-branch\" will continue fetching from and\n> integrating with their HEAD without storing any remote tracking\n> branch.\n\nAlternatively, if you can move the logic to set up this\nconfiguration further down so that it happens after we talked to the\nother side and figured out remote_head_points_at, you could instead\nset it up to keep a single remote tracking branch.\n\nEven if you did so, guess_remote_head() may not find any branch when\nthe other repository's HEAD is detached, so you would need to decide\nwhat to do in such a case, and \"fetch and integrate their HEAD\nwithout using any remote tracking branch\" may be a reasonable thing\nto do in such a case.\n"},{"id":"199031","messageId":"CACsJy8BLwd=XeHFqg5jb=yx3p6xdKuzvG+KvfotfLf0vfSZdRQ@mail.gmail.com","threadId":"31526","inReplyTo":"7vipbh6qxa.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-14T13:10:16Z","receivedAt":"2012-09-14T13:10:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Sep 14, 2012 at 1:48 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Who guarantees at this point in the codepath that option_branch is\n>> set when option_single_branch is non-zero?  Until we talk with the\n>> remote, \"clone --single-branch\" without an explicit \"--branch\" will\n>> not learn which branch at the remote we are going to fetch (it will\n>> be their HEAD).\n>>\n>> I wonder if this should be more like this:\n>>\n>>       if (option_single_branch) {\n>>               if (option_branch)\n>>                       Your patch \"+refs/heads/foo:refs/remotes/origin/foo\";\n>>               else\n>>                       \"HEAD\";\n>>         } else {\n>>               Original \"+refs/heads/*:refs/remotes/origin/*\";\n>>       }\n>>\n>> That is, \"clone --single-branch\" will continue fetching from and\n>> integrating with their HEAD without storing any remote tracking\n>> branch.\n>\n> Alternatively, if you can move the logic to set up this\n> configuration further down so that it happens after we talked to the\n> other side and figured out remote_head_points_at, you could instead\n> set it up to keep a single remote tracking branch.\n\nThat sounds reasonable. I have a question though, what should a user\ndo when he/she want to fetch all branches again? Messing up with\nrefspec in config file is not something I would like to do.\n\nPerhaps a heuristic in git-fetch to detect \"single branch\" situation\nand ignore refspec? We could hint people that refspecs are not\nfollowed when remote has more than one branch. They could either fetch\nthe another branch explicitly, which turns off the heuristic, or turn\noff the advice.\n\n> Even if you did so, guess_remote_head() may not find any branch when\n> the other repository's HEAD is detached, so you would need to decide\n> what to do in such a case, and \"fetch and integrate their HEAD\n> without using any remote tracking branch\" may be a reasonable thing\n> to do in such a case.\n-- \nDuy\n"},{"id":"199034","messageId":"CAN0XMOJFG2_Ju8fcAcyTiUc3owjedpKX+LpHdBWdym36Hm0LdA@mail.gmail.com","threadId":"31526","inReplyTo":"CACsJy8BLwd=XeHFqg5jb=yx3p6xdKuzvG+KvfotfLf0vfSZdRQ@mail.gmail.com","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-14T14:25:40Z","receivedAt":"2012-09-14T14:25:40Z","isPatch":true,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Fri, Sep 14, 2012 at 3:10 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Fri, Sep 14, 2012 at 1:48 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Junio C Hamano <gitster@pobox.com> writes:\n>>\n>>> Who guarantees at this point in the codepath that option_branch is\n>>> set when option_single_branch is non-zero?  Until we talk with the\n>>> remote, \"clone --single-branch\" without an explicit \"--branch\" will\n>>> not learn which branch at the remote we are going to fetch (it will\n>>> be their HEAD).\n>>>\n>>> I wonder if this should be more like this:\n>>>\n>>>       if (option_single_branch) {\n>>>               if (option_branch)\n>>>                       Your patch \"+refs/heads/foo:refs/remotes/origin/foo\";\n>>>               else\n>>>                       \"HEAD\";\n>>>         } else {\n>>>               Original \"+refs/heads/*:refs/remotes/origin/*\";\n>>>       }\n>>>\n>>> That is, \"clone --single-branch\" will continue fetching from and\n>>> integrating with their HEAD without storing any remote tracking\n>>> branch.\n>>\n>> Alternatively, if you can move the logic to set up this\n>> configuration further down so that it happens after we talked to the\n>> other side and figured out remote_head_points_at, you could instead\n>> set it up to keep a single remote tracking branch.\n>\n> That sounds reasonable. I have a question though, what should a user\n> do when he/she want to fetch all branches again? Messing up with\n> refspec in config file is not something I would like to do.\n>\n\n$ git remote set-branches <remote> \"*\"\n\n> Perhaps a heuristic in git-fetch to detect \"single branch\" situation\n> and ignore refspec? We could hint people that refspecs are not\n> followed when remote has more than one branch. They could either fetch\n> the another branch explicitly, which turns off the heuristic, or turn\n> off the advice.\n>\n\nSuch an advice when using \"--single-branch\" is a good idea, i think.\nSomething like \"The remote <remote> is configured to fetch only branch <branch>.\nIf you want to fetch all branches, use \"git remote set-branches <remote> \"*\"\"\nor something like that.\n\n>> Even if you did so, guess_remote_head() may not find any branch when\n>> the other repository's HEAD is detached, so you would need to decide\n>> what to do in such a case, and \"fetch and integrate their HEAD\n>> without using any remote tracking branch\" may be a reasonable thing\n>> to do in such a case.\n> --\n> Duy\n"},{"id":"199039","messageId":"7v7grw7ftj.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CACsJy8BLwd=XeHFqg5jb=yx3p6xdKuzvG+KvfotfLf0vfSZdRQ@mail.gmail.com","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T16:02:48Z","receivedAt":"2012-09-14T16:02:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Fri, Sep 14, 2012 at 1:48 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n>> Alternatively, if you can move the logic to set up this\n>> configuration further down so that it happens after we talked to the\n>> other side and figured out remote_head_points_at, you could instead\n>> set it up to keep a single remote tracking branch.\n>\n> That sounds reasonable. I have a question though, what should a user\n> do when he/she want to fetch all branches again? Messing up with\n> refspec in config file is not something I would like to do.\n\nYou first have to think ;-).\n\nI would say there are two kinds of users.\n\n - To the simplistic ones who fear the power of configuration, we\n   can simply tell \"You don't. Use 'single' when you want to keep\n   working with the single branch. If you want full, reclone, and\n   migrate your work from the single one by fetching from it to the\n   full clone before discarding the single one\".\n\n - To the ones who wants to take the full advantage of flexibility\n   of configuration, we can tell \"remotes.$name.fetch configuration\n   is your friend. Do whatever you want to do with it, but here are\n   two hints\".  The hints would cover the case to revert to the\n   default refspec, and the case to add another specific branch.\n\nThese days, with \"git config\" and \"git remote\" wrappers, I do not\nparticularly see a need to fear the power of configuration, though.\n"},{"id":"199064","messageId":"1347646267-25891-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"7vipbh6qxa.fsf@alter.siamese.dyndns.org","subject":"[PATCHv2] clone: fix refspec on \"--single-branch\" option","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-14T18:11:07Z","receivedAt":"2012-09-14T18:11:07Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After a repo was cloned with the \"--single-branch\"\noption, the configured refspec looks like\n\"+refs/heads/*:refs/remotes/origin/*\".\nAfter fetching from this repo again, it'll receive\nall refs instead of just the ref from the single\nbranch. Fixing this by configure exactly the ref\nof the branch the user specified in the \"git clone\"\ncommand.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\n> Alternatively, if you can move the logic to set up this\n> configuration further down so that it happens after we talked to the\n> other side and figured out remote_head_points_at, you could instead\n> set it up to keep a single remote tracking branch.\n>\n> Even if you did so, guess_remote_head() may not find any branch when\n> the other repository's HEAD is detached, so you would need to decide\n> what to do in such a case, and \"fetch and integrate their HEAD\n> without using any remote tracking branch\" may be a reasonable thing\n> to do in such a case.\n\nThis second version now covers also the \"--single-branch\"\noption when it was called without \"--branch\". It also covers\nthe \"detached HEAD\" case.\nI've tested all the use-cases that have been described above and it works\nas expected with this patch. But there's just one thing. It fetches\nalso all the tags even if they're not on this branch. I'm still in the\n\"learning process\", perhaps someone else can fix this problem or point\nme to the reason. \nI think it comes from \"transport_fetch_refs(transport, mapped_refs);\"\non line 813 which is called with a full \"+refs/heads/*:refs/remotes/origin/*\"\nrefspec. Thanks\n\n builtin/clone.c | 41 +++++++++++++++++++++++++++--------------\n 1 Datei geändert, 27 Zeilen hinzugefügt(+), 14 Zeilen entfernt(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..3ddf5ab 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -755,20 +755,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +839,33 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\tif (option_mirror || !option_bare) {\n+\t\tstrbuf_reset(&value);\n+\t\tif (option_single_branch) {\n+\t\t\tif (option_branch)\n+\t\t\t\tstrbuf_addf(&value, \"+%s%s:%s%s\", src_ref_prefix, option_branch,\n+\t\t\t\t\t\tbranch_top.buf, option_branch);\n+\t\t\telse if (remote_head_points_at)\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n+\t\t\t\t\t\t\t\t\tbranch_top.buf, prettify_refname(remote_head_points_at->name));\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\n-- \n1.7.12.395.g6b149ce.dirty\n"},{"id":"199067","messageId":"7vhar04fb6.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"7vipbh6qxa.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T18:42:05Z","receivedAt":"2012-09-14T18:42:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Alternatively, if you can move the logic to set up this\n> configuration further down so that it happens after we talked to the\n> other side and figured out remote_head_points_at, you could instead\n> set it up to keep a single remote tracking branch.\n>\n> Even if you did so, guess_remote_head() may not find any branch when\n> the other repository's HEAD is detached, so you would need to decide\n> what to do in such a case, and \"fetch and integrate their HEAD\n> without using any remote tracking branch\" may be a reasonable thing\n> to do in such a case.\n\nAlong the lines of this, perhaps.\n\n builtin/clone.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git i/builtin/clone.c w/builtin/clone.c\nindex 5e8f3ba..c9e099d 100644\n--- i/builtin/clone.c\n+++ w/builtin/clone.c\n@@ -853,6 +853,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\tif (option_single_branch) {\n+\t\t/* Fix up the refspec for fetch */\n+\t\tstrbuf_reset(&value);\n+\t\tif (!remote_head_points_at)\n+\t\t\tstrbuf_addf(&value, \"HEAD\");\n+\t\telse\n+\t\t\tstrbuf_addf(&value, \"%s:%s%s\",\n+\t\t\t\t    remote_head_points_at->name,\n+\t\t\t\t    branch_top.buf,\n+\t\t\t\t    skip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\n+\t\tstrbuf_reset(&key);\n+\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\tgit_config_set_multivar(key.buf, value.buf, NULL, 1);\n+\t}\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\n"},{"id":"199069","messageId":"7vd31o4dfu.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347646267-25891-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv2] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T19:22:29Z","receivedAt":"2012-09-14T19:22:29Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> +\t\t\telse if (remote_head_points_at)\n> +\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n> +\t\t\t\t\t\t\t\t\tbranch_top.buf, prettify_refname(remote_head_points_at->name));\n\nAs refspec maps names that appear on the source side to names that\nappear on the destination side, and for fetch, the \"soruce side\"\nis the remote, using \"our_head_points_at\" on the source side makes\nit look very fishy (even though it may be a name derived from\nremote_head_points_at and has the correct and appropriate value).\n\n\"prettify\" also is very questionable.  It is meant to strip commonly\nknown prefix to make it easier to read by humans, and we can change\nits result based solely on aesthetics in the future.  It is not\nsuitable for coming up with a value for configuration in the longer\nterm.\n\nCan we make the part you moved de-dented a bit, perhaps by making it\ninto a small helper function or something?  It is extremely hard to\nread with overly looooooong lines.\n"},{"id":"199074","messageId":"1347657228-16404-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"7vd31o4dfu.fsf@alter.siamese.dyndns.org","subject":"[PATCHv3] clone: fix refspec on \"--single-branch\" option","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-14T21:13:48Z","receivedAt":"2012-09-14T21:13:48Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After a repo was cloned with the \"--single-branch\"\noption, the configured refspec looks like\n\"+refs/heads/*:refs/remotes/origin/*\".\nAfter fetching from this repo again, it'll receive\nall refs instead of just the ref from the single\nbranch. Fixing this by configure exactly the ref\nof the branch the user specified in the \"git clone\"\ncommand.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n> As refspec maps names that appear on the source side to names that\n> appear on the destination side, and for fetch, the \"soruce side\"\n> is the remote, using \"our_head_points_at\" on the source side makes\n> it look very fishy (even though it may be a name derived from\n> remote_head_points_at and has the correct and appropriate value).\n>\n> \"prettify\" also is very questionable.  It is meant to strip commonly\n> known prefix to make it easier to read by humans, and we can change\n> its result based solely on aesthetics in the future.  It is not\n> suitable for coming up with a value for configuration in the longer\n> term.\n>\n\nThanks. I've fixed.\n\n> Can we make the part you moved de-dented a bit, perhaps by making it\n> into a small helper function or something?  It is extremely hard to\n> read with overly looooooong lines.\n>\n\nWould be nice but the list of arguments will become too long.\nI did a bit of reformating and think it now looks a bit nicer.\n\n builtin/clone.c | 43 +++++++++++++++++++++++++++++--------------\n 1 Datei geändert, 29 Zeilen hinzugefügt(+), 14 Zeilen entfernt(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..06e3d3a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -755,20 +755,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +839,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\tif (option_mirror || !option_bare) {\n+\t\tstrbuf_reset(&value);\n+\t\tif (option_single_branch) {\n+\t\t\tif (option_branch)\n+\t\t\t\tstrbuf_addf(&value, \"+%s%s:%s%s\",\n+\t\t\t\t\t\tsrc_ref_prefix, option_branch,\n+\t\t\t\t\t\tbranch_top.buf, option_branch);\n+\t\t\telse if (remote_head_points_at)\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\",\n+\t\t\t\t\t\tremote_head_points_at->name, branch_top.buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\n-- \n1.7.12.396.g3cff853.dirty\n"},{"id":"199081","messageId":"7vy5kc1avx.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347657228-16404-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv3] clone: fix refspec on \"--single-branch\" option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-14T22:45:54Z","receivedAt":"2012-09-14T22:45:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> +\tif (option_mirror || !option_bare) {\n> +\t\tstrbuf_reset(&value);\n> +\t\tif (option_single_branch) {\n> +\t\t\tif (option_branch)\n> +\t\t\t\tstrbuf_addf(&value, \"+%s%s:%s%s\",\n> +\t\t\t\t\t\tsrc_ref_prefix, option_branch,\n> +\t\t\t\t\t\tbranch_top.buf, option_branch);\n> +\t\t\telse if (remote_head_points_at)\n> +\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\",\n> +\t\t\t\t\t\tremote_head_points_at->name, branch_top.buf,\n> +\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n\nWe have already set \"remote.origin.url\" to this repository, so the\nnext \"git fetch\" would simply fetch from \"HEAD\" per default.\nPerhaps worth commenting that here?\n\nOther than that, looks good.  Perhaps we would want a test or two,\ntoo?\n\nThanks.\n"},{"id":"199140","messageId":"1347783184-20045-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"7vy5kc1avx.fsf@alter.siamese.dyndns.org","subject":"[PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-16T08:13:04Z","receivedAt":"2012-09-16T08:13:04Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After running \"git clone --single\", the resulting repository has the\nusual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\nrefspec installed, which means that a subsequent \"git fetch\" will\nend up grabbing all the other branches.\n\nUpdate the fetch refspec to cover only the singly cloned ref instead\nto correct this.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\nChanges to v3:\n- use commit message from Junio's topic branch\n- add comment for the 'detached HEAD' case (also from Junio's topic branch)\n(thanks for that)\n- add tests for the refspec installed by the clone command\n\n builtin/clone.c          | 49 ++++++++++++++++++++++++++++------------\n t/t5709-clone-refspec.sh | 59 ++++++++++++++++++++++++++++++++++++++++++++++++\n 2 Dateien geändert, 94 Zeilen hinzugefügt(+), 14 Zeilen entfernt(-)\n create mode 100755 t/t5709-clone-refspec.sh\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..be4c62b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -755,20 +755,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +839,41 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\tif (option_mirror || !option_bare) {\n+\t\tstrbuf_reset(&value);\n+\t\tif (option_single_branch) {\n+\t\t\tif (option_branch)\n+\t\t\t\tstrbuf_addf(&value, \"+%s%s:%s%s\",\n+\t\t\t\t\t\tsrc_ref_prefix, option_branch,\n+\t\t\t\t\t\tbranch_top.buf, option_branch);\n+\t\t\telse if (remote_head_points_at)\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\",\n+\t\t\t\t\t\tremote_head_points_at->name, branch_top.buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t\t/*\n+\t\t\t * otherwise, the next \"git fetch\" will\n+\t\t\t * simply fetch from HEAD without updating\n+\t\t\t * any remote tracking branch, which is what\n+\t\t\t * we want.\n+\t\t\t */\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\ndiff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\nnew file mode 100755\nindex 0000000..f4c8e31\n--- /dev/null\n+++ b/t/t5709-clone-refspec.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+\n+test_description='test refspec written by clone-command'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo one >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\techo two >file &&\n+\tgit commit -a -m two &&\n+\tgit tag two &&\n+\techo three >file &&\n+\tgit commit -a -m three &&\n+\tgit checkout -b foo &&\n+\techo four >file &&\n+\tgit commit -a -m four &&\n+\tgit checkout master\n+'\n+\n+test_expect_success 'refspec contains all branches by default' '\n+\tgit clone \"file://$PWD\" dir_all &&\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" > expected &&\n+\tgit --git-dir=dir_all/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'refspec contains only master with option --single-branch and remotes HEAD point to master' '\n+\tgit clone --single-branch \"file://$PWD\" dir_master &&\n+\techo \"+refs/heads/master:refs/remotes/origin/master\" > expected &&\n+\tgit --git-dir=dir_master/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'refspec contains only foo with option --single-branch and remotes HEAD point to foo' '\n+\tgit checkout foo &&\n+\tgit clone --single-branch \"file://$PWD\" dir_foo &&\n+\techo \"+refs/heads/foo:refs/remotes/origin/foo\" > expected &&\n+\tgit --git-dir=dir_foo/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'refspec contains one branch after using option --single-branch with --branch' '\n+\tgit checkout master &&\n+\tgit clone --single-branch --branch foo \"file://$PWD\" dir_foo2 &&\n+\techo \"+refs/heads/foo:refs/remotes/origin/foo\" > expected &&\n+\tgit --git-dir=dir_foo2/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'no refspec is written if remotes HEAD is detached' '\n+\tgit checkout two^ &&\n+\tgit clone --single-branch \"file://$PWD\" dir_detached &&\n+\trm expected && touch expected &&\n+\tgit --git-dir=dir_detached/.git config --get remote.origin.fetch > actual\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.7.12.396.g6bea32d.dirty\n"},{"id":"199180","messageId":"7vobl5xnjx.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347783184-20045-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T04:48:02Z","receivedAt":"2012-09-17T04:48:02Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> - add tests for the refspec installed by the clone command\n\nThanks.\n\n> +test_expect_success 'refspec contains all branches by default' '\n> +\tgit clone \"file://$PWD\" dir_all &&\n\nThere have been numerous \"on windows which should we use, $PWD or\n$(pwd)?\" gotchas.  In this particular case, wouldn't the tests work\nequally well with \".\" to avoid the uneasiness factor?\n\n> +\techo \"+refs/heads/*:refs/remotes/origin/*\" > expected &&\n> +\tgit --git-dir=dir_all/.git config --get remote.origin.fetch > actual &&\n> +\ttest_cmp expected actual\n\nI am a bit torn with this one.\n\nWe'd want to make sure a single clone from a repository with two\nbranches will not result in a repository where the next fetch will\nnot pull in the other branch, and the value of the\n\"remote.origin.fetch\" is an implementation detail that happen to be\nwhat affects the outcome.  The test is not checking the expected\noutcome in a direct way (imagine how this test will break when we do\nanother change similar to the migration from .git/remotes/origin to\nthe remote.origin.fetch variables).\n\nI'll let it pass for now, though.\n\n> +test_expect_success 'no refspec is written if remotes HEAD is detached' '\n> +\tgit checkout two^ &&\n> +\tgit clone --single-branch \"file://$PWD\" dir_detached &&\n> +\trm expected && touch expected &&\n\nIf earlier tests failed, there may not be any expected file and the\n\"rm expected\" will fail.  You can just say\n\n\t>expected &&\n\ninstead.  \"touch\" is a way to update the timestamp of the file; do\nnot use it when you want to make sure an empty file exists.\n\n> +\tgit --git-dir=dir_detached/.git config --get remote.origin.fetch > actual\n> +\ttest_cmp expected actual\n> +'\n\nI'd feel better if the test were like this instead:\n\n\tgit checkout two^ &&\n        git clone --single-branch . dir_detached &&\n        (\n\t\tcd dir_detached &&\n                git fetch &&\n                git for-each-ref refs/remotes/origin >actual\n\t) &&\n        >expect &&\n        test_cmp expect actual\n\nThat is what I would call \"testing the desired results in the most\ndirect way\".\n\nPerhaps like this?\n\n-- >8 -- t5709-clone-refspec.sh -- >8 --\n#!/bin/sh\n\ntest_description='test refspec written by clone-command'\n. ./test-lib.sh\n\ntest_expect_success 'setup' '\n\t# Make two branches, \"master\" and \"side\"\n\techo one >file &&\n\tgit add file &&\n\tgit commit -m one &&\n\techo two >file &&\n\tgit commit -a -m two &&\n\tgit tag two &&\n\techo three >file &&\n\tgit commit -a -m three &&\n\tgit checkout -b side &&\n\techo four >file &&\n\tgit commit -a -m four &&\n\tgit checkout master &&\n\n\t# default clone\n\tgit clone . dir_all &&\n\n\t# default --single that follows HEAD=master\n\tgit clone --single-branch . dir_master &&\n\n\t# default --single that follows HEAD=side\n\tgit checkout side &&\n\tgit clone --single-branch . dir_side &&\n\n\t# explicit --single that follows side\n\tgit checkout master &&\n\tgit clone --single-branch --branch side . dir_side2 &&\n\n\t# --single that does not know what branch to follow\n\tgit checkout two^ &&\n\tgit clone --single-branch . dir_detached &&\n\n\t# advance both \"master\" and \"side\" branches\n\tgit checkout side &&\n\techo five >file &&\n\tgit commit -a -m five &&\n\tgit checkout master &&\n\techo six >file &&\n\tgit commit -a -m six\n'\n\ntest_expect_success 'by default all branches will be kept updated' '\n\t(\n\t\tcd dir_all && git fetch &&\n\t\tgit for-each-ref refs/remotes/origin |\n\t\tsed -e \"/HEAD$/d\" \\\n\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n\t) &&\n\t# follow both master and side\n\tgit for-each-ref refs/heads >expect &&\n\ttest_cmp expect actual\n'\n\ntest_expect_success '--single-branch while HEAD pointing at master' '\n\t(\n\t\tcd dir_master && git fetch &&\n\t\tgit for-each-ref refs/remotes/origin |\n\t\tsed -e \"/HEAD$/d\" \\\n\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n\t) &&\n\t# only follow master\n\tgit for-each-ref refs/heads/master >expect &&\n\ttest_cmp expect actual\n'\n\ntest_expect_success '--single-branch while HEAD pointing at side' '\n\t(\n\t\tcd dir_side && git fetch &&\n\t\tgit for-each-ref refs/remotes/origin |\n\t\tsed -e \"/HEAD$/d\" \\\n\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n\t) &&\n\t# only follow side\n\tgit for-each-ref refs/heads/side >expect &&\n\ttest_cmp expect actual\n'\n\ntest_expect_success '--single-branch with explicit --branch side' '\n\t(\n\t\tcd dir_side2 && git fetch &&\n\t\tgit for-each-ref refs/remotes/origin |\n\t\tsed -e \"/HEAD$/d\" \\\n\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n\t) &&\n\t# only follow side\n\tgit for-each-ref refs/heads/side >expect &&\n\ttest_cmp expect actual\n'\n\ntest_expect_success '--single-branch with detached' '\n\t(\n\t\tcd dir_detached && git fetch &&\n\t\tgit for-each-ref refs/remotes/origin |\n\t\tsed -e \"/HEAD$/d\" \\\n\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n\t)\n\t# nothing\n\t>expect &&\n\ttest_cmp expect actual\n'\n\ntest_done\n"},{"id":"199217","messageId":"CACsJy8Bsps_-RaXdBd9ipWPvJukjK7mRN_wG2ezEL5d4JLyx+g@mail.gmail.com","threadId":"31526","inReplyTo":"1347783184-20045-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-17T12:06:06Z","receivedAt":"2012-09-17T12:06:06Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 16, 2012 at 3:13 PM, Ralf Thielow <ralf.thielow@gmail.com> wrote:\n> +       if (option_mirror || !option_bare) {\n> +               strbuf_reset(&value);\n\nI think we should use a new strbuf local variable here to avoid\nresetting this. At least reviewers don't have to check if this\nstatememt causes any effect later on because \"value\"'s value is gone.\n\n> +               if (option_single_branch) {\n> +                       if (option_branch)\n> +                               strbuf_addf(&value, \"+%s%s:%s%s\",\n> +                                               src_ref_prefix, option_branch,\n> +                                               branch_top.buf, option_branch);\n> +                       else if (remote_head_points_at)\n> +                               strbuf_addf(&value, \"+%s:%s%s\",\n> +                                               remote_head_points_at->name, branch_top.buf,\n> +                                               skip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n> +                       /*\n> +                        * otherwise, the next \"git fetch\" will\n> +                        * simply fetch from HEAD without updating\n> +                        * any remote tracking branch, which is what\n> +                        * we want.\n> +                        */\n\nMaybe document updates too? Though if it's obvious that\n--single-branch should prepare refspec so that only one branch is\nfetched later on, then maybe not.\n\n> +               } else {\n> +                       strbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n> +               }\n\n--mirror --single-branch combination does not look right. The \"heads/\"\npart is missing..\n\n$ git branch\n  master * wildmatch\n$ LANG=C ./git clone --mirror --single-branch .git abc\nCloning into bare repository 'abc'...\ndone.\n$ grep fetch abc/config\n        fetch = +refs/heads/wildmatch:refs/wildmatch\n$ rm -rf abc\n$ LANG=C ./git clone --mirror --single-branch --branch=master .git abc\nCloning into bare repository 'abc'...\ndone.\n$ grep fetch abc/config\n        fetch = +refs/master:refs/master\n-- \nDuy\n"},{"id":"199218","messageId":"CACsJy8BGBwNp-oDsnB1QObrVLU54rtDmGGBF=Muww8ZJjfZScA@mail.gmail.com","threadId":"31526","inReplyTo":"CACsJy8Bsps_-RaXdBd9ipWPvJukjK7mRN_wG2ezEL5d4JLyx+g@mail.gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-17T12:11:11Z","receivedAt":"2012-09-17T12:11:11Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Sep 17, 2012 at 7:06 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> --mirror --single-branch combination does not look right. The \"heads/\"\n> part is missing..\n\nIt also does not look right for cloning a tag:\n\n$ LANG=C ./git clone --single-branch --branch=v1.7.0 .git abc\nCloning into 'abc'...\ndone.\nNote: checking out 'e923eaeb901ff056421b9007adcbbce271caa7b6'.\n\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by performing another checkout.\n\nIf you want to create a new branch to retain commits you create, you may\ndo so (now or later) by using -b with the checkout command again. Example:\n\n  git checkout -b new_branch_name\n\n$ grep fetch abc/.git/config\n        fetch = +refs/heads/v1.7.0:refs/remotes/origin/v1.7.0\n-- \nDuy\n"},{"id":"199226","messageId":"CAN0XMOJrFOWR4_Cdx5fJz2zKCf=mSKBKV16A0dy1f5DptdOMVA@mail.gmail.com","threadId":"31526","inReplyTo":"CACsJy8Bsps_-RaXdBd9ipWPvJukjK7mRN_wG2ezEL5d4JLyx+g@mail.gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-17T13:25:20Z","receivedAt":"2012-09-17T13:25:20Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Mon, Sep 17, 2012 at 2:06 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Sun, Sep 16, 2012 at 3:13 PM, Ralf Thielow <ralf.thielow@gmail.com> wrote:\n>> +       if (option_mirror || !option_bare) {\n>> +               strbuf_reset(&value);\n>\n> I think we should use a new strbuf local variable here to avoid\n> resetting this. At least reviewers don't have to check if this\n> statememt causes any effect later on because \"value\"'s value is gone.\n>\n\nIt seems that we don't need this reset here because it's already\ndone earlier in this function. The variable \"key\" is also used multiple\ntimes so I wouldn't use a new variable.\n\n>> +               if (option_single_branch) {\n>> +                       if (option_branch)\n>> +                               strbuf_addf(&value, \"+%s%s:%s%s\",\n>> +                                               src_ref_prefix, option_branch,\n>> +                                               branch_top.buf, option_branch);\n>> +                       else if (remote_head_points_at)\n>> +                               strbuf_addf(&value, \"+%s:%s%s\",\n>> +                                               remote_head_points_at->name, branch_top.buf,\n>> +                                               skip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n>> +                       /*\n>> +                        * otherwise, the next \"git fetch\" will\n>> +                        * simply fetch from HEAD without updating\n>> +                        * any remote tracking branch, which is what\n>> +                        * we want.\n>> +                        */\n>\n> Maybe document updates too? Though if it's obvious that\n> --single-branch should prepare refspec so that only one branch is\n> fetched later on, then maybe not.\n>\n\nI think it's obvious.\n\n>> +               } else {\n>> +                       strbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n>> +               }\n>\n> --mirror --single-branch combination does not look right. The \"heads/\"\n> part is missing..\n>\n> $ git branch\n>   master * wildmatch\n> $ LANG=C ./git clone --mirror --single-branch .git abc\n> Cloning into bare repository 'abc'...\n> done.\n> $ grep fetch abc/config\n>         fetch = +refs/heads/wildmatch:refs/wildmatch\n> $ rm -rf abc\n> $ LANG=C ./git clone --mirror --single-branch --branch=master .git abc\n> Cloning into bare repository 'abc'...\n> done.\n> $ grep fetch abc/config\n>         fetch = +refs/master:refs/master\n> --\n> Duy\n\nThanks, I'll check this later and send a new version.\n"},{"id":"199270","messageId":"1347909706-22888-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"CACsJy8BGBwNp-oDsnB1QObrVLU54rtDmGGBF=Muww8ZJjfZScA@mail.gmail.com","subject":"[PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-17T19:21:46Z","receivedAt":"2012-09-17T19:21:46Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After running \"git clone --single\", the resulting repository has the\nusual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\nrefspec installed, which means that a subsequent \"git fetch\" will\nend up grabbing all the other branches.\n\nUpdate the fetch refspec to cover only the singly cloned ref instead\nto correct this.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\nchanges in v5:\n- extract a function to write refspec config\n- handle --mirror option (test added)\n- install correct refspec if the value of --branch is a tag (test added)\nThanks to Junio for:\n- refactor tests\n- add tests for created refs \n\nI'm not happy about using \"our_head_points_to\" for the remote\npart of the refspec. Junio already complaint about that. As far\nas I can see it's the only way of getting the information that\nit's a tag. Also the condition \"is this a tag\" might not be the\nbest way.\n\n builtin/clone.c          |  66 +++++++++++++++-----\n t/t5709-clone-refspec.sh | 156 +++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 208 insertions(+), 14 deletions(-)\n create mode 100755 t/t5709-clone-refspec.sh\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..431635c 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -610,6 +610,55 @@ static void write_config(struct string_list *config)\n \t}\n }\n \n+static void write_refspec_config(const char* src_ref_prefix,\n+\t\tconst struct ref* our_head_points_at,\n+\t\tconst struct ref* remote_head_points_at, struct strbuf* branch_top)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n+\n+\tif (option_mirror || !option_bare) {\n+\t\tif (option_single_branch && !option_mirror) {\n+\t\t\tif (option_branch) {\n+\t\t\t\tif (strstr(our_head_points_at->name, \"refs/tags/\"))\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s\", our_head_points_at->name,\n+\t\t\t\t\t\tour_head_points_at->name);\n+\t\t\t\telse\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf, option_branch);\n+\t\t\t} else if (remote_head_points_at) {\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", remote_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * otherwise, the next \"git fetch\" will\n+\t\t\t * simply fetch from HEAD without updating\n+\t\t\t * any remote tracking branch, which is what\n+\t\t\t * we want.\n+\t\t\t */\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top->buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&key);\n+\tstrbuf_release(&value);\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -755,20 +804,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +888,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\twrite_refspec_config(src_ref_prefix, our_head_points_at,\n+\t\t\tremote_head_points_at, &branch_top);\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\ndiff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\nnew file mode 100755\nindex 0000000..af45182\n--- /dev/null\n+++ b/t/t5709-clone-refspec.sh\n@@ -0,0 +1,156 @@\n+#!/bin/sh\n+\n+test_description='test refspec written by clone-command'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\t# Make two branches, \"master\" and \"side\"\n+\techo one >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\techo two >file &&\n+\tgit commit -a -m two &&\n+\tgit tag two &&\n+\techo three >file &&\n+\tgit commit -a -m three &&\n+\tgit checkout -b side &&\n+\techo four >file &&\n+\tgit commit -a -m four &&\n+\tgit checkout master &&\n+\n+\t# default clone\n+\tgit clone . dir_all &&\n+\n+\t# default --single that follows HEAD=master\n+\tgit clone --single-branch . dir_master &&\n+\n+\t# default --single that follows HEAD=side\n+\tgit checkout side &&\n+\tgit clone --single-branch . dir_side &&\n+\n+\t# explicit --single that follows side\n+\tgit checkout master &&\n+\tgit clone --single-branch --branch side . dir_side2 &&\n+\n+\t# default --single with --mirror\n+\tgit clone --single-branch --mirror . dir_mirror &&\n+\n+\t# --single that does not know what branch to follow\n+\tgit checkout two^ &&\n+\tgit clone --single-branch . dir_detached &&\n+\n+\t# explicit --single with tag\n+\tgit clone --single-branch --branch two . dir_tag &&\n+\n+\t# advance both \"master\" and \"side\" branches\n+\tgit checkout side &&\n+\techo five >file &&\n+\tgit commit -a -m five &&\n+\tgit checkout master &&\n+\techo six >file &&\n+\tgit commit -a -m six\n+'\n+\n+test_expect_success 'refspec contains all branches by default' '\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" > expect &&\n+\tgit --git-dir=dir_all/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'refspec contains all refs with option --mirror' '\n+\techo \"+refs/*:refs/*\" > expect &&\n+\tgit --git-dir=dir_mirror config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'refspec contains tag ref' '\n+\techo \"+refs/tags/two:refs/tags/two\" > expect &&\n+\tgit --git-dir=dir_tag/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'refspec contains only master with option --single-branch and remotes HEAD point to master' '\n+\techo \"+refs/heads/master:refs/remotes/origin/master\" > expect &&\n+\tgit --git-dir=dir_master/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'refspec contains only foo with option --single-branch and remotes HEAD point to side' '\n+\techo \"+refs/heads/side:refs/remotes/origin/side\" > expect &&\n+\tgit --git-dir=dir_side/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'refspec contains one branch after using option --single-branch with --branch' '\n+\techo \"+refs/heads/side:refs/remotes/origin/side\" > expect &&\n+\tgit --git-dir=dir_side2/.git config --get remote.origin.fetch > actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'no refspec is written if remotes HEAD is detached' '\n+\t> expect &&\n+\tgit --git-dir=dir_detached/.git config --get remote.origin.fetch > actual\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'by default all branches will be kept updated' '\n+\t(\n+\t\tcd dir_all && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# follow both master and side\n+\tgit for-each-ref refs/heads >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at master' '\n+\t(\n+\t\tcd dir_master && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow master\n+\tgit for-each-ref refs/heads/master >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at side' '\n+\t(\n+\t\tcd dir_side && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch side' '\n+\t(\n+\t\tcd dir_side2 && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with detached' '\n+\t(\n+\t\tcd dir_detached && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t)\n+\t# nothing\n+\t>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.7.12.397.ge29f79e.dirty\n"},{"id":"199273","messageId":"7vhaqwv2co.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CACsJy8Bsps_-RaXdBd9ipWPvJukjK7mRN_wG2ezEL5d4JLyx+g@mail.gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T20:08:55Z","receivedAt":"2012-09-17T20:08:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> --mirror --single-branch combination does not look right. The \"heads/\"\n> part is missing..\n\nWhat does it supposed to do in the first place?  \"mirror\" is\nprimarily about grabbing everything without leaving stuff out, and\n\"single\" is about grabbing only one thing without other stuff.  It\nsmells like a nonsense case we didn't even bother to catch and\ndiagnose as an error to me.\n\nBesides, mirror implies bare, so we would expect to see\n\n    refs/heads/$single:refs/heads/$single\n\nand that matches what the \"single\" gives us without \"mirror\", no?\n\nHow about explicitly forbidding that combination, or ignoring mirror\nwhen single is given?\n"},{"id":"199274","messageId":"7vd31kv2ba.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CACsJy8BGBwNp-oDsnB1QObrVLU54rtDmGGBF=Muww8ZJjfZScA@mail.gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T20:09:45Z","receivedAt":"2012-09-17T20:09:45Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Mon, Sep 17, 2012 at 7:06 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>> --mirror --single-branch combination does not look right. The \"heads/\"\n>> part is missing..\n>\n> It also does not look right for cloning a tag:\n>\n> $ LANG=C ./git clone --single-branch --branch=v1.7.0 .git abc\n\nWhat does it even _mean_ to clone a single branch and give a tag,\nnot a branch, to the --branch option?  Again, shouldn't it be\ndiagnosed as an error in the first place?\n"},{"id":"199276","messageId":"7v8vc8v1wd.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347909706-22888-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T20:18:42Z","receivedAt":"2012-09-17T20:18:42Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> - handle --mirror option (test added)\n\nHandle how?  I personally think erroring out is the right way to\nhandle it, but if we care about people who have been misusing the\ncombination of single and mirror, the second best way would be to\nimply \"mirror\" and \"single\" combination as \"bare\" and \"single\"\nwithout \"mirror\".\n\n> - install correct refspec if the value of --branch is a tag (test added)\n\nWhat is the definition of \"correct\"?  I see the documentation says\n\"--branch can also take tags and treat them like detached HEAD\", and\neven though I _think_ allowing tags was a huge mistake, if we claim\nwe treat it like detached HEAD, we should do the same when coming up\nwith the refspec used for the follow-up \"fetch\".\n\nWhatever we decide to do, the semantics we decided to use at least\nneed to be described in the commit log message, not just in the\n\"changes from the previous iteration\".  Updating the\n\"Documentation/git-clone.txt\" would also be necessary.\n\n> +test_expect_success 'refspec contains all branches by default' '\n> +\techo \"+refs/heads/*:refs/remotes/origin/*\" > expect &&\n> +\tgit --git-dir=dir_all/.git config --get remote.origin.fetch > actual &&\n> +\ttest_cmp expect actual\n> +'\n\nI still think these checks that know the current implementation\ndetails (i.e. what exact configuration variables get what exact\nvalues) are wrong thing to have in the longer term.  If the desired\nbehaviour (i.e. \"later fetch do not screw up\") can be tested\ndirectly like the later parts of the test in this patch does, how\nthat desired behaviour is implemented should not have to be cast in\nstone with these tests.\n"},{"id":"199288","messageId":"CAN0XMOKCsjfG-DgV_rr99mYXHBSKryL1O46X+7r5ie+=2aKmmA@mail.gmail.com","threadId":"31526","inReplyTo":"7v8vc8v1wd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-17T21:04:09Z","receivedAt":"2012-09-17T21:04:09Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Mon, Sep 17, 2012 at 10:18 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Ralf Thielow <ralf.thielow@gmail.com> writes:\n>\n>> - handle --mirror option (test added)\n>\n> Handle how?  I personally think erroring out is the right way to\n> handle it, but if we care about people who have been misusing the\n> combination of single and mirror, the second best way would be to\n> imply \"mirror\" and \"single\" combination as \"bare\" and \"single\"\n> without \"mirror\".\n>\n\nWith this patch it would be \"bare\" and \"single\", and with the next\nfetch a \"mirror\". However, the combination of \"single\" and \"mirror\"\ndoesn't really make sense so I also think we should error it out.\n\n>> - install correct refspec if the value of --branch is a tag (test added)\n>\n> What is the definition of \"correct\"?  I see the documentation says\n> \"--branch can also take tags and treat them like detached HEAD\", and\n> even though I _think_ allowing tags was a huge mistake, if we claim\n> we treat it like detached HEAD, we should do the same when coming up\n> with the refspec used for the follow-up \"fetch\".\n>\n\nThis patch would install the refspec \"+refs/tags/v1.7.10:refs/tags/v1.7.10\",\nso we would do the same with the follow-up \"fetch\", not?\nThis can be seen as \"it's not a bug, it's a feature\". Why not cloning a\ntag of a repo if someone just want to build a tag without having anything else.\n\n> Whatever we decide to do, the semantics we decided to use at least\n> need to be described in the commit log message, not just in the\n> \"changes from the previous iteration\".  Updating the\n> \"Documentation/git-clone.txt\" would also be necessary.\n>\n\nOk.\n\n>> +test_expect_success 'refspec contains all branches by default' '\n>> +     echo \"+refs/heads/*:refs/remotes/origin/*\" > expect &&\n>> +     git --git-dir=dir_all/.git config --get remote.origin.fetch > actual &&\n>> +     test_cmp expect actual\n>> +'\n>\n> I still think these checks that know the current implementation\n> details (i.e. what exact configuration variables get what exact\n> values) are wrong thing to have in the longer term.  If the desired\n> behaviour (i.e. \"later fetch do not screw up\") can be tested\n> directly like the later parts of the test in this patch does, how\n> that desired behaviour is implemented should not have to be cast in\n> stone with these tests.\n\nYou wrote\n> I'll let it pass for now, though.\nso I haven't removed them yet. I'll delete them.\n"},{"id":"199297","messageId":"7vlig8s50o.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CAN0XMOKCsjfG-DgV_rr99mYXHBSKryL1O46X+7r5ie+=2aKmmA@mail.gmail.com","subject":"Re: [PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T21:39:35Z","receivedAt":"2012-09-17T21:39:35Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n>>> - install correct refspec if the value of --branch is a tag (test added)\n>>\n>> What is the definition of \"correct\"?  I see the documentation says\n>> \"--branch can also take tags and treat them like detached HEAD\", and\n>> even though I _think_ allowing tags was a huge mistake, if we claim\n>> we treat it like detached HEAD, we should do the same when coming up\n>> with the refspec used for the follow-up \"fetch\".\n>>\n>\n> This patch would install the refspec \"+refs/tags/v1.7.10:refs/tags/v1.7.10\",\n> so we would do the same with the follow-up \"fetch\", not?\n> This can be seen as \"it's not a bug, it's a feature\". Why not cloning a\n> tag of a repo if someone just want to build a tag without having anything else.\n\nEven though I did say I thought allowing tags was a huge mistake, I\nwas not suggesting to break existing users by making such a clone\ninto an error.\n\nBut the main point of the discussion is what should happen upon the\nnext \"git fetch\" in the repository, no?  Shouldn't the refspec be\nthe same as the case where you \"clone --single\" a repository whose\nHEAD is detached at v1.7.10 in that example, instead of saying\n\"fetch the same tag over and over\"?  After all that is the way I\nexpect that most people would read \"treat them line detached HEAD\"\nin the documentation.  Subsequent \"git fetch\" would fetch from the\nHEAD of the upstream just like a clone from a repository with a\ndetached HEAD.\n\nThe above is *not* a rhetorical question; I just do not think of a\nsensible reason why we want a refspec that says \"keep updating the\nv1.7.10 tag, as it might change on the other end, and do not bother\nwhat other things that happen in that upstream repository\".  What\nsensible workflow would benefit from such a refspec?\n"},{"id":"199315","messageId":"CACsJy8CENp5VasbizzrLRbRELaaPD-gVLepEdXHPv=Diq9s6sg@mail.gmail.com","threadId":"31526","inReplyTo":"7vhaqwv2co.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-18T01:02:13Z","receivedAt":"2012-09-18T01:02:13Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 18, 2012 at 3:08 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> --mirror --single-branch combination does not look right. The \"heads/\"\n>> part is missing..\n>\n> What does it supposed to do in the first place?  \"mirror\" is\n> primarily about grabbing everything without leaving stuff out, and\n> \"single\" is about grabbing only one thing without other stuff.  It\n> smells like a nonsense case we didn't even bother to catch and\n> diagnose as an error to me.\n>\n> Besides, mirror implies bare, so we would expect to see\n>\n>     refs/heads/$single:refs/heads/$single\n>\n> and that matches what the \"single\" gives us without \"mirror\", no?\n\nYes that's what I think it should do too.\n\n> How about explicitly forbidding that combination, or ignoring mirror\n> when single is given?\n\nI see nothing wrong with mirroring just one branch. But if handling it\nis too complex than forbidding it is fine.\n-- \nDuy\n"},{"id":"199316","messageId":"CACsJy8CUuPPvTMNmtX9P5ZHkvUHCVZkKmPj7Ghubxt_daPaKZg@mail.gmail.com","threadId":"31526","inReplyTo":"7vd31kv2ba.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-18T01:04:10Z","receivedAt":"2012-09-18T01:04:10Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 18, 2012 at 3:09 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Mon, Sep 17, 2012 at 7:06 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>>> --mirror --single-branch combination does not look right. The \"heads/\"\n>>> part is missing..\n>>\n>> It also does not look right for cloning a tag:\n>>\n>> $ LANG=C ./git clone --single-branch --branch=v1.7.0 .git abc\n>\n> What does it even _mean_ to clone a single branch and give a tag,\n> not a branch, to the --branch option?  Again, shouldn't it be\n> diagnosed as an error in the first place?\n\nThat could be misleading. The original purpose is to clone just one\nref, be it a branch or a tag. You might want to work on top of a\nspecific version and may not want to clone everything. For cloning a\ntag, we're put in detached head state immediately.\n-- \nDuy\n"},{"id":"199325","messageId":"7vwqzsoufi.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CACsJy8CUuPPvTMNmtX9P5ZHkvUHCVZkKmPj7Ghubxt_daPaKZg@mail.gmail.com","subject":"Re: [PATCHv4] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T03:56:33Z","receivedAt":"2012-09-18T03:56:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Tue, Sep 18, 2012 at 3:09 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> On Mon, Sep 17, 2012 at 7:06 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>>>> --mirror --single-branch combination does not look right. The \"heads/\"\n>>>> part is missing..\n>>>\n>>> It also does not look right for cloning a tag:\n>>>\n>>> $ LANG=C ./git clone --single-branch --branch=v1.7.0 .git abc\n>>\n>> What does it even _mean_ to clone a single branch and give a tag,\n>> not a branch, to the --branch option?  Again, shouldn't it be\n>> diagnosed as an error in the first place?\n>\n> That could be misleading. The original purpose is to clone just one\n> ref, be it a branch or a tag. You might want to work on top of a\n> specific version and may not want to clone everything.\n\nYeah, see my other comments.\n"},{"id":"199370","messageId":"CAN0XMOKCZL+X1QNW7CPmu-wdpVN0HjK=3z-sTz5naSQ5RW3x4w@mail.gmail.com","threadId":"31526","inReplyTo":"7vlig8s50o.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-18T14:08:03Z","receivedAt":"2012-09-18T14:08:03Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Mon, Sep 17, 2012 at 11:39 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Ralf Thielow <ralf.thielow@gmail.com> writes:\n>\n>>>> - install correct refspec if the value of --branch is a tag (test added)\n>>>\n>>> What is the definition of \"correct\"?  I see the documentation says\n>>> \"--branch can also take tags and treat them like detached HEAD\", and\n>>> even though I _think_ allowing tags was a huge mistake, if we claim\n>>> we treat it like detached HEAD, we should do the same when coming up\n>>> with the refspec used for the follow-up \"fetch\".\n>>>\n>>\n>> This patch would install the refspec \"+refs/tags/v1.7.10:refs/tags/v1.7.10\",\n>> so we would do the same with the follow-up \"fetch\", not?\n>> This can be seen as \"it's not a bug, it's a feature\". Why not cloning a\n>> tag of a repo if someone just want to build a tag without having anything else.\n>\n> Even though I did say I thought allowing tags was a huge mistake, I\n> was not suggesting to break existing users by making such a clone\n> into an error.\n>\n> But the main point of the discussion is what should happen upon the\n> next \"git fetch\" in the repository, no?  Shouldn't the refspec be\n> the same as the case where you \"clone --single\" a repository whose\n> HEAD is detached at v1.7.10 in that example, instead of saying\n> \"fetch the same tag over and over\"?  After all that is the way I\n> expect that most people would read \"treat them line detached HEAD\"\n> in the documentation.  Subsequent \"git fetch\" would fetch from the\n> HEAD of the upstream just like a clone from a repository with a\n> detached HEAD.\n>\n\nIMO if a user uses \"clone --single-branch --branch <tag>\", then he/she\nwants to have this tag only. Why should the next \"git fetch\" fetching\nsomething different?\nI read \"treat them like detached HEAD\" in the way that the resulting\nrepo is in a \"detached HEAD\" state.\n\n> The above is *not* a rhetorical question; I just do not think of a\n> sensible reason why we want a refspec that says \"keep updating the\n> v1.7.10 tag, as it might change on the other end, and do not bother\n> what other things that happen in that upstream repository\".  What\n> sensible workflow would benefit from such a refspec?\n>\n>\n\nWhen using a tag in a different meaning than \"tag this version and\ndo never change it\", for example if someone uses a tag to describe\nwhich revision of the project was lastly delivered to the customer, then\nthey could use a tag \"delivered\". After a new version was delivered,\nsomeone changes this tag from \"revx\" to \"revy\". Having such a refspec,\nyou can fetch this tag and always get the delivered version.\nThis example is very theoretically.\n"},{"id":"199376","messageId":"7vipbbnu9p.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"CAN0XMOKCZL+X1QNW7CPmu-wdpVN0HjK=3z-sTz5naSQ5RW3x4w@mail.gmail.com","subject":"Re: [PATCHv5] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T16:57:38Z","receivedAt":"2012-09-18T16:57:38Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> IMO if a user uses \"clone --single-branch --branch <tag>\", then he/she\n> wants to have this tag only. Why should the next \"git fetch\" fetching\n> something different?\n\nOK, I can buy that.\n"},{"id":"199389","messageId":"1347995660-9956-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"1347909706-22888-1-git-send-email-ralf.thielow@gmail.com","subject":"[PATCHv6] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-18T19:14:20Z","receivedAt":"2012-09-18T19:14:20Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After running \"git clone --single\", the resulting repository has the\nusual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\nrefspec installed, which means that a subsequent \"git fetch\" will\nend up grabbing all the other branches.\n\nUpdate the fetch refspec to cover only the singly cloned ref instead\nto correct this.\n\nThat means:\nIf \"--single\" is used without \"--branch\" or \"--mirror\", the\nfetch refspec covers the branch on which remote's HEAD points to.\nIf \"--single\" is used with \"--branch\", it'll cover only the branch\nspecified in the \"--branch\" option.\nIf \"--single\" is combined with \"--mirror\", then it'll cover all\nrefs of the cloned repository.\nIf \"--single\" is used with \"--branch\" that specifies a tag, then\nit'll cover only the ref for this tag.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\nchanges in v6\n- remove initial created tests (they tested in a too deep level)\n- add tests for \"--mirror\" option\n- add tests for the case of cloning a tag\n- update commit message\n\nI've tried to update \"Documentation/git-clone.txt\", but I don't\nknow in which way this patch changes already described behaviour.\nThe resulting refspec seems only be covered in the last part of\nthe \"--single-branch\" section by describing \"--no-single-branch\",\nbut this hasn't changed. Or did I miss something?\n\n builtin/clone.c          |  66 ++++++++++++++++-----\n t/t5709-clone-refspec.sh | 145 +++++++++++++++++++++++++++++++++++++++++++++++\n 2 Dateien geändert, 197 Zeilen hinzugefügt(+), 14 Zeilen entfernt(-)\n create mode 100755 t/t5709-clone-refspec.sh\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..431635c 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -610,6 +610,55 @@ static void write_config(struct string_list *config)\n \t}\n }\n \n+static void write_refspec_config(const char* src_ref_prefix,\n+\t\tconst struct ref* our_head_points_at,\n+\t\tconst struct ref* remote_head_points_at, struct strbuf* branch_top)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n+\n+\tif (option_mirror || !option_bare) {\n+\t\tif (option_single_branch && !option_mirror) {\n+\t\t\tif (option_branch) {\n+\t\t\t\tif (strstr(our_head_points_at->name, \"refs/tags/\"))\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s\", our_head_points_at->name,\n+\t\t\t\t\t\tour_head_points_at->name);\n+\t\t\t\telse\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf, option_branch);\n+\t\t\t} else if (remote_head_points_at) {\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", remote_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * otherwise, the next \"git fetch\" will\n+\t\t\t * simply fetch from HEAD without updating\n+\t\t\t * any remote tracking branch, which is what\n+\t\t\t * we want.\n+\t\t\t */\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top->buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&key);\n+\tstrbuf_release(&value);\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -755,20 +804,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +888,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\twrite_refspec_config(src_ref_prefix, our_head_points_at,\n+\t\t\tremote_head_points_at, &branch_top);\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\ndiff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\nnew file mode 100755\nindex 0000000..8d32a97\n--- /dev/null\n+++ b/t/t5709-clone-refspec.sh\n@@ -0,0 +1,145 @@\n+#!/bin/sh\n+\n+test_description='test refspec written by clone-command'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\t# Make two branches, \"master\" and \"side\"\n+\techo one >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\techo two >file &&\n+\tgit commit -a -m two &&\n+\tgit tag two &&\n+\techo three >file &&\n+\tgit commit -a -m three &&\n+\tgit checkout -b side &&\n+\techo four >file &&\n+\tgit commit -a -m four &&\n+\tgit checkout master &&\n+\n+\t# default clone\n+\tgit clone . dir_all &&\n+\n+\t# default --single that follows HEAD=master\n+\tgit clone --single-branch . dir_master &&\n+\n+\t# default --single that follows HEAD=side\n+\tgit checkout side &&\n+\tgit clone --single-branch . dir_side &&\n+\n+\t# explicit --single that follows side\n+\tgit checkout master &&\n+\tgit clone --single-branch --branch side . dir_side2 &&\n+\n+\t# default --single with --mirror\n+\tgit clone --single-branch --mirror . dir_mirror &&\n+\n+\t# default --single with --branch and --mirror\n+\tgit clone --single-branch --mirror --branch side . dir_mirror_side &&\n+\n+\t# --single that does not know what branch to follow\n+\tgit checkout two^ &&\n+\tgit clone --single-branch . dir_detached &&\n+\n+\t# explicit --single with tag\n+\tgit clone --single-branch --branch two . dir_tag &&\n+\n+\t# advance both \"master\" and \"side\" branches\n+\tgit checkout side &&\n+\techo five >file &&\n+\tgit commit -a -m five &&\n+\tgit checkout master &&\n+\techo six >file &&\n+\tgit commit -a -m six\n+'\n+\n+test_expect_success 'by default all branches will be kept updated' '\n+\t(\n+\t\tcd dir_all && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# follow both master and side\n+\tgit for-each-ref refs/heads >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at master' '\n+\t(\n+\t\tcd dir_master && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow master\n+\tgit for-each-ref refs/heads/master >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at side' '\n+\t(\n+\t\tcd dir_side && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch side' '\n+\t(\n+\t\tcd dir_side2 && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+\n+test_expect_success '--single-branch with explicit --branch tag' '\n+\t(\n+\t\tcd dir_tag && git fetch &&\n+\t\tgit for-each-ref refs/tags >../actual\n+\t) &&\n+\tgit for-each-ref refs/tags >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with --mirror' '\n+\t(\n+\t\tcd dir_mirror && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch and --mirror' '\n+\t(\n+\t\tcd dir_mirror_side && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with detached' '\n+\t(\n+\t\tcd dir_detached && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t)\n+\t# nothing\n+\t>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.7.12.396.g7954078\n"},{"id":"199393","messageId":"7vk3vrm83a.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347995660-9956-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv6] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T19:42:01Z","receivedAt":"2012-09-18T19:42:01Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> After running \"git clone --single\", the resulting repository has the\n> usual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\n> refspec installed, which means that a subsequent \"git fetch\" will\n> end up grabbing all the other branches.\n>\n> Update the fetch refspec to cover only the singly cloned ref instead\n> to correct this.\n>\n> That means:\n> If \"--single\" is used without \"--branch\" or \"--mirror\", the\n> fetch refspec covers the branch on which remote's HEAD points to.\n> If \"--single\" is used with \"--branch\", it'll cover only the branch\n> specified in the \"--branch\" option.\n> If \"--single\" is combined with \"--mirror\", then it'll cover all\n> refs of the cloned repository.\n> If \"--single\" is used with \"--branch\" that specifies a tag, then\n> it'll cover only the ref for this tag.\n>\n> Signed-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n> ---\n>\n> changes in v6\n> - remove initial created tests (they tested in a too deep level)\n> - add tests for \"--mirror\" option\n> - add tests for the case of cloning a tag\n> - update commit message\n>\n> I've tried to update \"Documentation/git-clone.txt\", but I don't\n> know in which way this patch changes already described behaviour.\n> The resulting refspec seems only be covered in the last part of\n> the \"--single-branch\" section by describing \"--no-single-branch\",\n> but this hasn't changed. Or did I miss something?\n\nI do not think we are changing anything.  The whole \"--single-branch\"\nwas an half-baked afterthought hack that nobody anticipated the user\nto later issue \"git fetch\" in the resulting repository, and everybody\ninvolved in the series (the maintainer included) were just happy to\nsee the resulting code only transferred objects needed for the branch\nwithout wasting bandwidth for objects needed for other branches, and\nstopped thinking beyond that X-<.\n\nIf anything, we need to _add_ the description of what happens when\nfurther fetches are done.  The second and the third paragraph in\nthe DESCRIPTION section talks only about the normal case, so at\nleast at the end of the second paragraph we should say \"But if you\nuse --single-branch, all of this is different\".  And what happens\nwhen you do use --single-branch should be described in the part that\ndescribes that option, e.g.\n\n Documentation/git-clone.txt | 15 ++++++++++++---\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git c/Documentation/git-clone.txt w/Documentation/git-clone.txt\nindex c1ddd4c..f59bc49 100644\n--- c/Documentation/git-clone.txt\n+++ w/Documentation/git-clone.txt\n@@ -29,7 +29,8 @@ currently active branch.\n After the clone, a plain `git fetch` without arguments will update\n all the remote-tracking branches, and a `git pull` without\n arguments will in addition merge the remote master branch into the\n-current master branch, if any.\n+current master branch, if any (this is untrue when \"--single-branch\"\n+is given; see below).\n \n This default configuration is achieved by creating references to\n the remote branch heads under `refs/remotes/origin` and\n@@ -152,9 +153,11 @@ objects from the source repository into a pack in the cloned repository.\n -b <name>::\n \tInstead of pointing the newly created HEAD to the branch pointed\n \tto by the cloned repository's HEAD, point to `<name>` branch\n-\tinstead. `--branch` can also take tags and treat them like\n-\tdetached HEAD. In a non-bare repository, this is the branch\n+\tinstead. In a non-bare repository, this is the branch\n \tthat will be checked out.\n++\n+`--branch` can also take tags and detaches the HEAD\n+at that commit in the resulting repository. \n \n --upload-pack <upload-pack>::\n -u <upload-pack>::\n@@ -193,6 +196,12 @@ objects from the source repository into a pack in the cloned repository.\n \tclone with the `--depth` option, this is the default, unless\n \t`--no-single-branch` is given to fetch the histories near the\n \ttips of all branches.\n++\n+Further fetches into the resulting repository will only update the\n+remote tracking branch for the branch this option was used for the\n+initial cloning.  If the HEAD at the remote did not point at any\n+branch when `--single-branch` clone was made, no remote tracking\n+branch is created.\n \n --recursive::\n --recurse-submodules::\n"},{"id":"199394","messageId":"7vfw6fm7xb.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1347995660-9956-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv6] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T19:45:36Z","receivedAt":"2012-09-18T19:45:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> + ...\n> +\t# explicit --single with tag\n> +\tgit clone --single-branch --branch two . dir_tag &&\n> +\n> +\t# advance both \"master\" and \"side\" branches\n> +\tgit checkout side &&\n> +\techo five >file &&\n> +\tgit commit -a -m five &&\n> +\tgit checkout master &&\n> +\techo six >file &&\n> +\tgit commit -a -m six\n\nDon't we also want to cover your \"delivery\" tag scenario by updating\nthe tag \"two\" before letting the clones fetch in the following test?\n\n> + ...\n> +test_expect_success '--single-branch with explicit --branch tag' '\n> +\t(\n> +\t\tcd dir_tag && git fetch &&\n> +\t\tgit for-each-ref refs/tags >../actual\n> +\t) &&\n> +\tgit for-each-ref refs/tags >expect &&\n> +\ttest_cmp expect actual\n> +'\n"},{"id":"199452","messageId":"CACsJy8AjCC2C2dGt+G0V1FZWVJg1f=QGetTJdB4-JVEs8f4DBw@mail.gmail.com","threadId":"31526","inReplyTo":"1347995660-9956-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv6] clone --single: limit the fetch refspec to fetched branch","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-19T07:36:14Z","receivedAt":"2012-09-19T07:36:14Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Sep 19, 2012 at 2:14 AM, Ralf Thielow <ralf.thielow@gmail.com> wrote:\n> +test_expect_success '--single-branch with explicit --branch tag' '\n> +       (\n> +               cd dir_tag && git fetch &&\n> +               git for-each-ref refs/tags >../actual\n> +       ) &&\n> +       git for-each-ref refs/tags >expect &&\n> +       test_cmp expect actual\n> +'\n\nWe should have added the tag right after cloning, not until the first\ngit-fetch. Not that I object how you do it in this patch. Just a note\nto myself that if I'm going to do that, I'll need to update this test\nto update the change tag before fetching and verify the tag is updated\nafter git-fetch.\n-- \nDuy\n"},{"id":"199454","messageId":"CAN0XMOKxOsXZ_gHy-MAwzwS5j-6npd8YeiTmbGeKU73zwAD7ag@mail.gmail.com","threadId":"31526","inReplyTo":"CACsJy8AjCC2C2dGt+G0V1FZWVJg1f=QGetTJdB4-JVEs8f4DBw@mail.gmail.com","subject":"Re: [PATCHv6] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-19T08:24:10Z","receivedAt":"2012-09-19T08:24:10Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"On Wed, Sep 19, 2012 at 9:36 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On Wed, Sep 19, 2012 at 2:14 AM, Ralf Thielow <ralf.thielow@gmail.com> wrote:\n>> +test_expect_success '--single-branch with explicit --branch tag' '\n>> +       (\n>> +               cd dir_tag && git fetch &&\n>> +               git for-each-ref refs/tags >../actual\n>> +       ) &&\n>> +       git for-each-ref refs/tags >expect &&\n>> +       test_cmp expect actual\n>> +'\n>\n> We should have added the tag right after cloning, not until the first\n> git-fetch. Not that I object how you do it in this patch. Just a note\n> to myself that if I'm going to do that, I'll need to update this test\n> to update the change tag before fetching and verify the tag is updated\n> after git-fetch.\n> --\n> Duy\n\nRight. I'll create two tests to replace this one. The first test\nverifys that the tag\nhas been added after cloning. And in the second test, the tag gets updated in\nthe cloned repository and we verify that the next fetch updates the tag, which\ncatches my \"delivery\" tag example.\n"},{"id":"199481","messageId":"1348073136-8842-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"7vfw6fm7xb.fsf@alter.siamese.dyndns.org","subject":"[PATCHv7] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-19T16:45:36Z","receivedAt":"2012-09-19T16:45:36Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After running \"git clone --single\", the resulting repository has the\nusual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\nrefspec installed, which means that a subsequent \"git fetch\" will\nend up grabbing all the other branches.\n\nUpdate the fetch refspec to cover only the singly cloned ref instead\nto correct this.\n\nThat means:\nIf \"--single\" is used without \"--branch\" or \"--mirror\", the\nfetch refspec covers the branch on which remote's HEAD points to.\nIf \"--single\" is used with \"--branch\", it'll cover only the branch\nspecified in the \"--branch\" option.\nIf \"--single\" is combined with \"--mirror\", then it'll cover all\nrefs of the cloned repository.\nIf \"--single\" is used with \"--branch\" that specifies a tag, then\nit'll cover only the ref for this tag.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\nChanges in v7:\n- remove a test which has shown that after the next \"git fetch\"\n  the tags will be the same (was actually a bit senseless)\n- add a test to show that the initial clone with \"--single\" fetches tags\n- add a test to show when using \"--single\" with \"--branch\"\n  which points to a tag, then the next \"git fetch\" will update\n  this tag if it was updated in the cloned repository\n- update documentation of \"git clone\" (Thanks to Junio)\n\n Documentation/git-clone.txt |  15 +++--\n builtin/clone.c             |  66 +++++++++++++++----\n t/t5709-clone-refspec.sh    | 155 ++++++++++++++++++++++++++++++++++++++++++++\n 3 Dateien geändert, 218 Zeilen hinzugefügt(+), 18 Zeilen entfernt(-)\n create mode 100755 t/t5709-clone-refspec.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex c1ddd4c..6d98ef3 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -29,7 +29,8 @@ currently active branch.\n After the clone, a plain `git fetch` without arguments will update\n all the remote-tracking branches, and a `git pull` without\n arguments will in addition merge the remote master branch into the\n-current master branch, if any.\n+current master branch, if any (this is untrue when \"--single-branch\"\n+is given; see below).\n \n This default configuration is achieved by creating references to\n the remote branch heads under `refs/remotes/origin` and\n@@ -152,9 +153,10 @@ objects from the source repository into a pack in the cloned repository.\n -b <name>::\n \tInstead of pointing the newly created HEAD to the branch pointed\n \tto by the cloned repository's HEAD, point to `<name>` branch\n-\tinstead. `--branch` can also take tags and treat them like\n-\tdetached HEAD. In a non-bare repository, this is the branch\n-\tthat will be checked out.\n+\tinstead. In a non-bare repository, this is the branch that will\n+\tbe checked out.\n+\t`--branch` can also take tags and detaches the HEAD at that commit\n+\tin the resulting repository.\n \n --upload-pack <upload-pack>::\n -u <upload-pack>::\n@@ -193,6 +195,11 @@ objects from the source repository into a pack in the cloned repository.\n \tclone with the `--depth` option, this is the default, unless\n \t`--no-single-branch` is given to fetch the histories near the\n \ttips of all branches.\n+\tFurther fetches into the resulting repository will only update the\n+\tremote tracking branch for the branch this option was used for the\n+\tinitial cloning.  If the HEAD at the remote did not point at any\n+\tbranch when `--single-branch` clone was made, no remote tracking\n+\tbranch is created.\n \n --recursive::\n --recurse-submodules::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..431635c 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -610,6 +610,55 @@ static void write_config(struct string_list *config)\n \t}\n }\n \n+static void write_refspec_config(const char* src_ref_prefix,\n+\t\tconst struct ref* our_head_points_at,\n+\t\tconst struct ref* remote_head_points_at, struct strbuf* branch_top)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n+\n+\tif (option_mirror || !option_bare) {\n+\t\tif (option_single_branch && !option_mirror) {\n+\t\t\tif (option_branch) {\n+\t\t\t\tif (strstr(our_head_points_at->name, \"refs/tags/\"))\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s\", our_head_points_at->name,\n+\t\t\t\t\t\tour_head_points_at->name);\n+\t\t\t\telse\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf, option_branch);\n+\t\t\t} else if (remote_head_points_at) {\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", remote_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * otherwise, the next \"git fetch\" will\n+\t\t\t * simply fetch from HEAD without updating\n+\t\t\t * any remote tracking branch, which is what\n+\t\t\t * we want.\n+\t\t\t */\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top->buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_reset(&key);\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&key);\n+\tstrbuf_release(&value);\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -755,20 +804,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +888,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\twrite_refspec_config(src_ref_prefix, our_head_points_at,\n+\t\t\tremote_head_points_at, &branch_top);\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\ndiff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\nnew file mode 100755\nindex 0000000..69a0779\n--- /dev/null\n+++ b/t/t5709-clone-refspec.sh\n@@ -0,0 +1,155 @@\n+#!/bin/sh\n+\n+test_description='test refspec written by clone-command'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\t# Make two branches, \"master\" and \"side\"\n+\techo one >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\techo two >file &&\n+\tgit commit -a -m two &&\n+\tgit tag two &&\n+\techo three >file &&\n+\tgit commit -a -m three &&\n+\tgit checkout -b side &&\n+\techo four >file &&\n+\tgit commit -a -m four &&\n+\tgit checkout master &&\n+\n+\t# default clone\n+\tgit clone . dir_all &&\n+\n+\t# default --single that follows HEAD=master\n+\tgit clone --single-branch . dir_master &&\n+\n+\t# default --single that follows HEAD=side\n+\tgit checkout side &&\n+\tgit clone --single-branch . dir_side &&\n+\n+\t# explicit --single that follows side\n+\tgit checkout master &&\n+\tgit clone --single-branch --branch side . dir_side2 &&\n+\n+\t# default --single with --mirror\n+\tgit clone --single-branch --mirror . dir_mirror &&\n+\n+\t# default --single with --branch and --mirror\n+\tgit clone --single-branch --mirror --branch side . dir_mirror_side &&\n+\n+\t# --single that does not know what branch to follow\n+\tgit checkout two^ &&\n+\tgit clone --single-branch . dir_detached &&\n+\n+\t# explicit --single with tag\n+\tgit clone --single-branch --branch two . dir_tag &&\n+\n+\t# advance both \"master\" and \"side\" branches\n+\tgit checkout side &&\n+\techo five >file &&\n+\tgit commit -a -m five &&\n+\tgit checkout master &&\n+\techo six >file &&\n+\tgit commit -a -m six\n+'\n+\n+test_expect_success 'by default all branches will be kept updated' '\n+\t(\n+\t\tcd dir_all && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# follow both master and side\n+\tgit for-each-ref refs/heads >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at master' '\n+\t(\n+\t\tcd dir_master && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow master\n+\tgit for-each-ref refs/heads/master >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at side' '\n+\t(\n+\t\tcd dir_side && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch side' '\n+\t(\n+\t\tcd dir_side2 && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+\n+test_expect_success '--single-branch fetches tags' '\n+\t(\n+\t\tcd dir_all &&\n+\t\tgit for-each-ref refs/tags >../actual\n+\t) &&\n+\tgit for-each-ref refs/tags >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '\n+\tgit tag -d two && git tag two &&\n+\t(\n+\t\tcd dir_tag && git fetch &&\n+\t\tgit for-each-ref refs/tags >../actual\n+\t) &&\n+\tgit for-each-ref refs/tags >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with --mirror' '\n+\t(\n+\t\tcd dir_mirror && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch and --mirror' '\n+\t(\n+\t\tcd dir_mirror_side && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with detached' '\n+\t(\n+\t\tcd dir_detached && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t)\n+\t# nothing\n+\t>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.7.12.396.g7954078\n"},{"id":"199545","messageId":"7vzk4legrz.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1348073136-8842-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv7] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-19T23:26:08Z","receivedAt":"2012-09-19T23:26:08Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ralf Thielow <ralf.thielow@gmail.com> writes:\n\n> diff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\n> new file mode 100755\n> index 0000000..69a0779\n> --- /dev/null\n> +++ b/t/t5709-clone-refspec.sh\n> @@ -0,0 +1,155 @@\n> +#!/bin/sh\n> +\n> +test_description='test refspec written by clone-command'\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\t# Make two branches, \"master\" and \"side\"\n> + ...\n> +\t# explicit --single with tag\n> +\tgit clone --single-branch --branch two . dir_tag &&\n> +\n> +\t# advance both \"master\" and \"side\" branches\n> +\tgit checkout side &&\n> +\techo five >file &&\n> +\tgit commit -a -m five &&\n> +\tgit checkout master &&\n> +\techo six >file &&\n> +\tgit commit -a -m six\n> +'\n> +\n> + ...\n> +test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '\n> +\tgit tag -d two && git tag two &&\n\nCurious.  Was it insufficient to update this tag at the end of the\n\"setup\" process like everybody else where\n\n\t# advance both \"master\" and \"side\" branches\n\nhappens?\n\nNot that it is wrong to do this, but it looked somewhat\ninconsistent.\n"},{"id":"199600","messageId":"1348164248-18756-1-git-send-email-ralf.thielow@gmail.com","threadId":"31526","inReplyTo":"7vzk4legrz.fsf@alter.siamese.dyndns.org","subject":"[PATCHv8] clone --single: limit the fetch refspec to fetched branch","fromName":"Ralf Thielow","fromEmail":"ralf.thielow@gmail.com","sentAt":"2012-09-20T18:04:08Z","receivedAt":"2012-09-20T18:04:08Z","isPatch":false,"sender":{"key":"ralf.thielow@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1275832?v=4"},"body":"After running \"git clone --single\", the resulting repository has the\nusual default \"+refs/heads/*:refs/remotes/origin/*\" wildcard fetch\nrefspec installed, which means that a subsequent \"git fetch\" will\nend up grabbing all the other branches.\n\nUpdate the fetch refspec to cover only the singly cloned ref instead\nto correct this.\n\nThat means:\nIf \"--single\" is used without \"--branch\" or \"--mirror\", the\nfetch refspec covers the branch on which remote's HEAD points to.\nIf \"--single\" is used with \"--branch\", it'll cover only the branch\nspecified in the \"--branch\" option.\nIf \"--single\" is combined with \"--mirror\", then it'll cover all\nrefs of the cloned repository.\nIf \"--single\" is used with \"--branch\" that specifies a tag, then\nit'll cover only the ref for this tag.\n\nSigned-off-by: Ralf Thielow <ralf.thielow@gmail.com>\n---\n\nchanges in v8:\n- remove unnecessary strbuf_reset\n- remove test from v7 which has shown that tags get fetched when cloning\n- add test to show that tags will not being updated when using simple \"git clone\"\n- change position of the tag in the setup, not directly in the test\n\n Documentation/git-clone.txt |  15 +++--\n builtin/clone.c             |  65 ++++++++++++++----\n t/t5709-clone-refspec.sh    | 156 ++++++++++++++++++++++++++++++++++++++++++++\n 3 Dateien geändert, 218 Zeilen hinzugefügt(+), 18 Zeilen entfernt(-)\n create mode 100755 t/t5709-clone-refspec.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex c1ddd4c..6d98ef3 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -29,7 +29,8 @@ currently active branch.\n After the clone, a plain `git fetch` without arguments will update\n all the remote-tracking branches, and a `git pull` without\n arguments will in addition merge the remote master branch into the\n-current master branch, if any.\n+current master branch, if any (this is untrue when \"--single-branch\"\n+is given; see below).\n \n This default configuration is achieved by creating references to\n the remote branch heads under `refs/remotes/origin` and\n@@ -152,9 +153,10 @@ objects from the source repository into a pack in the cloned repository.\n -b <name>::\n \tInstead of pointing the newly created HEAD to the branch pointed\n \tto by the cloned repository's HEAD, point to `<name>` branch\n-\tinstead. `--branch` can also take tags and treat them like\n-\tdetached HEAD. In a non-bare repository, this is the branch\n-\tthat will be checked out.\n+\tinstead. In a non-bare repository, this is the branch that will\n+\tbe checked out.\n+\t`--branch` can also take tags and detaches the HEAD at that commit\n+\tin the resulting repository.\n \n --upload-pack <upload-pack>::\n -u <upload-pack>::\n@@ -193,6 +195,11 @@ objects from the source repository into a pack in the cloned repository.\n \tclone with the `--depth` option, this is the default, unless\n \t`--no-single-branch` is given to fetch the histories near the\n \ttips of all branches.\n+\tFurther fetches into the resulting repository will only update the\n+\tremote tracking branch for the branch this option was used for the\n+\tinitial cloning.  If the HEAD at the remote did not point at any\n+\tbranch when `--single-branch` clone was made, no remote tracking\n+\tbranch is created.\n \n --recursive::\n --recurse-submodules::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 5e8f3ba..047239c 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -610,6 +610,54 @@ static void write_config(struct string_list *config)\n \t}\n }\n \n+static void write_refspec_config(const char* src_ref_prefix,\n+\t\tconst struct ref* our_head_points_at,\n+\t\tconst struct ref* remote_head_points_at, struct strbuf* branch_top)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n+\n+\tif (option_mirror || !option_bare) {\n+\t\tif (option_single_branch && !option_mirror) {\n+\t\t\tif (option_branch) {\n+\t\t\t\tif (strstr(our_head_points_at->name, \"refs/tags/\"))\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s\", our_head_points_at->name,\n+\t\t\t\t\t\tour_head_points_at->name);\n+\t\t\t\telse\n+\t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf, option_branch);\n+\t\t\t} else if (remote_head_points_at) {\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", remote_head_points_at->name,\n+\t\t\t\t\t\tbranch_top->buf,\n+\t\t\t\t\t\tskip_prefix(remote_head_points_at->name, \"refs/heads/\"));\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * otherwise, the next \"git fetch\" will\n+\t\t\t * simply fetch from HEAD without updating\n+\t\t\t * any remote tracking branch, which is what\n+\t\t\t * we want.\n+\t\t\t */\n+\t\t} else {\n+\t\t\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top->buf);\n+\t\t}\n+\t\t/* Configure the remote */\n+\t\tif (value.len) {\n+\t\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n+\t\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\t\tstrbuf_reset(&key);\n+\n+\t\t\tif (option_mirror) {\n+\t\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n+\t\t\t\tgit_config_set(key.buf, \"true\");\n+\t\t\t\tstrbuf_reset(&key);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tstrbuf_release(&key);\n+\tstrbuf_release(&value);\n+}\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n@@ -755,20 +803,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n-\tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\t}\n-\n \tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n \tgit_config_set(key.buf, repo);\n \tstrbuf_reset(&key);\n@@ -853,6 +887,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n+\twrite_refspec_config(src_ref_prefix, our_head_points_at,\n+\t\t\tremote_head_points_at, &branch_top);\n+\n \tif (is_local)\n \t\tclone_local(path, git_dir);\n \telse if (refs && complete_refs_before_fetch)\ndiff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh\nnew file mode 100755\nindex 0000000..6f1ea98\n--- /dev/null\n+++ b/t/t5709-clone-refspec.sh\n@@ -0,0 +1,156 @@\n+#!/bin/sh\n+\n+test_description='test refspec written by clone-command'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\t# Make two branches, \"master\" and \"side\"\n+\techo one >file &&\n+\tgit add file &&\n+\tgit commit -m one &&\n+\techo two >file &&\n+\tgit commit -a -m two &&\n+\tgit tag two &&\n+\techo three >file &&\n+\tgit commit -a -m three &&\n+\tgit checkout -b side &&\n+\techo four >file &&\n+\tgit commit -a -m four &&\n+\tgit checkout master &&\n+\n+\t# default clone\n+\tgit clone . dir_all &&\n+\n+\t# default --single that follows HEAD=master\n+\tgit clone --single-branch . dir_master &&\n+\n+\t# default --single that follows HEAD=side\n+\tgit checkout side &&\n+\tgit clone --single-branch . dir_side &&\n+\n+\t# explicit --single that follows side\n+\tgit checkout master &&\n+\tgit clone --single-branch --branch side . dir_side2 &&\n+\n+\t# default --single with --mirror\n+\tgit clone --single-branch --mirror . dir_mirror &&\n+\n+\t# default --single with --branch and --mirror\n+\tgit clone --single-branch --mirror --branch side . dir_mirror_side &&\n+\n+\t# --single that does not know what branch to follow\n+\tgit checkout two^ &&\n+\tgit clone --single-branch . dir_detached &&\n+\n+\t# explicit --single with tag\n+\tgit clone --single-branch --branch two . dir_tag &&\n+\n+\t# advance both \"master\" and \"side\" branches\n+\tgit checkout side &&\n+\techo five >file &&\n+\tgit commit -a -m five &&\n+\tgit checkout master &&\n+\techo six >file &&\n+\tgit commit -a -m six &&\n+\n+\t# update tag\n+\tgit tag -d two && git tag two\n+'\n+\n+test_expect_success 'by default all branches will be kept updated' '\n+\t(\n+\t\tcd dir_all && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# follow both master and side\n+\tgit for-each-ref refs/heads >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'by default no tags will be kept updated' '\n+\t(\n+\t\tcd dir_all && git fetch &&\n+\t\tgit for-each-ref refs/tags >../actual\n+\t) &&\n+\tgit for-each-ref refs/tags >expect &&\n+\ttest_must_fail test_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at master' '\n+\t(\n+\t\tcd dir_master && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow master\n+\tgit for-each-ref refs/heads/master >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch while HEAD pointing at side' '\n+\t(\n+\t\tcd dir_side && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch side' '\n+\t(\n+\t\tcd dir_side2 && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t) &&\n+\t# only follow side\n+\tgit for-each-ref refs/heads/side >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '\n+\t(\n+\t\tcd dir_tag && git fetch &&\n+\t\tgit for-each-ref refs/tags >../actual\n+\t) &&\n+\tgit for-each-ref refs/tags >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with --mirror' '\n+\t(\n+\t\tcd dir_mirror && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with explicit --branch and --mirror' '\n+\t(\n+\t\tcd dir_mirror_side && git fetch &&\n+\t\tgit for-each-ref refs > ../actual\n+\t) &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with detached' '\n+\t(\n+\t\tcd dir_detached && git fetch &&\n+\t\tgit for-each-ref refs/remotes/origin |\n+\t\tsed -e \"/HEAD$/d\" \\\n+\t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n+\t)\n+\t# nothing\n+\t>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.7.12.396.g7954078\n"},{"id":"199629","messageId":"7v8vc49yy8.fsf@alter.siamese.dyndns.org","threadId":"31526","inReplyTo":"1348164248-18756-1-git-send-email-ralf.thielow@gmail.com","subject":"Re: [PATCHv8] clone --single: limit the fetch refspec to fetched branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-20T21:17:03Z","receivedAt":"2012-09-20T21:17:03Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks.\n"}]}