{"thread":{"id":"29298","subject":"[PATCH] clone: allow detached checkout when --branch takes a tag","startedAt":"2012-01-05T13:49:40Z","lastAt":"2012-01-24T07:01:59Z","messageCount":68,"participants":["Nguyễn Thái Ngọc Duy","Jeff King","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"181973","messageId":"1325771380-18862-1-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":null,"subject":"[PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-05T13:49:40Z","receivedAt":"2012-01-05T13:49:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This allows you to do \"git clone --branch=v1.7.8 git.git\" and work\nright away from there. No big deal, just one more convenient step, I\nthink. --branch taking a tag may be confusing though.\n\nWe can still have master in this case instead of detached HEAD, which\nmay make more sense because we use --branch. I don't care much which\nway should be used.\n\nLike? Dislike?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   20 +++++++++++++++++++-\n 1 files changed, 19 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 8f29912..97af4bd 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -23,6 +23,7 @@\n #include \"branch.h\"\n #include \"remote.h\"\n #include \"run-command.h\"\n+#include \"tag.h\"\n \n /*\n  * Overall FIXMEs:\n@@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tstrbuf_release(&head);\n \n \t\t\tif (!our_head_points_at) {\n+\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n+\t\t\t\tstrbuf_addstr(&head, option_branch);\n+\t\t\t\tour_head_points_at =\n+\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n+\t\t\t\tstrbuf_release(&head);\n+\t\t\t}\n+\n+\t\t\tif (!our_head_points_at) {\n \t\t\t\twarning(_(\"Remote branch %s not found in \"\n \t\t\t\t\t\"upstream %s, using HEAD instead\"),\n \t\t\t\t\toption_branch, option_origin);\n@@ -750,7 +759,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t      reflog_msg.buf);\n \t}\n \n-\tif (our_head_points_at) {\n+\tif (our_head_points_at &&\n+\t    !prefixcmp(our_head_points_at->name, \"refs/tags/\")) {\n+\t\tconst struct ref *ref = our_head_points_at;\n+\t\tstruct object *o;\n+\n+\t\t/* Detached HEAD */\n+\t\to = deref_tag(parse_object(ref->old_sha1), NULL, 0);\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", o->sha1, NULL,\n+\t\t\t   REF_NODEREF, DIE_ON_ERR);\n+\t} else if (our_head_points_at) {\n \t\t/* Local default branch link */\n \t\tcreate_symref(\"HEAD\", our_head_points_at->name, NULL);\n \t\tif (!option_bare) {\n-- \n1.7.8.36.g69ee2\n"},{"id":"181975","messageId":"20120105141844.GA26771@sigill.intra.peff.net","threadId":"29298","inReplyTo":"1325771380-18862-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-01-05T14:18:44Z","receivedAt":"2012-01-05T14:18:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jan 05, 2012 at 08:49:40PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> This allows you to do \"git clone --branch=v1.7.8 git.git\" and work\n> right away from there. No big deal, just one more convenient step, I\n> think. --branch taking a tag may be confusing though.\n> \n> We can still have master in this case instead of detached HEAD, which\n> may make more sense because we use --branch. I don't care much which\n> way should be used.\n> \n> Like? Dislike?\n\nSeems like a reasonable goal to me. I agree that \"--branch=v1.7.8\" is a\nlittle confusing, but not the end of the world. If we were designing it\nfrom scratch, I might call it \"--head\" or \"--checkout\" or something to\nindicate that it is what we are putting in HEAD. But I don't know that\nit is worth renaming the option or adding a new option.\n\n> @@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\t\tstrbuf_release(&head);\n>  \n>  \t\t\tif (!our_head_points_at) {\n> +\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n> +\t\t\t\tstrbuf_addstr(&head, option_branch);\n> +\t\t\t\tour_head_points_at =\n> +\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n> +\t\t\t\tstrbuf_release(&head);\n> +\t\t\t}\n> +\n> +\t\t\tif (!our_head_points_at) {\n\nHmm. The context just above your patch that got snipped does this:\n\n    strbuf_addstr(&head, src_ref_prefix);\n    strbuf_addstr(&head, option_branch);\n    our_head_points_at =\n        find_ref_by_name(mapped_refs, head.buf);\n\nwhere src_ref_prefix typically is \"refs/heads/\", and clearly you are\nmeaning to do the same thing for tags. But the use of \"src_ref_prefix\"\nis interesting.\n\nIt is always \"refs/heads/\" unless we are cloning into a bare mirror, in\nwhich case it is \"refs/\". So with your patch in the non-mirror case,\ndoing \"--branch=foo\" would try \"refs/heads/foo\" followed by\n\"refs/tags/foo\". Which makes sense. But in the mirror case, it will try\n\"refs/foo\" followed by \"refs/tags/foo\", which is kind of odd.\n\nI wonder, though, if the original code makes any sense. By using\n\"refs/\", I would have to say \"--branch=heads/foo\", which is kind of\nweird and undocumented. I think it should probably always be\n\"refs/heads/\", no matter if we are mirroring or not.\n\n> @@ -750,7 +759,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\t\t      reflog_msg.buf);\n>  \t}\n>  \n> -\tif (our_head_points_at) {\n> +\tif (our_head_points_at &&\n> +\t    !prefixcmp(our_head_points_at->name, \"refs/tags/\")) {\n\nI think I would prefer this check to be:\n\n  prefixcmp(our_head_points_at->name, \"refs/heads/\")\n\nwhich more closely matches the rules for what is allowed to go in HEAD\nas a symbolic ref. It's pretty hard to get something other than heads or\ntags, but you can do it with \"git clone --bare --mirror --branch=foo/bar\".\nI did argue above for doing away with that \"feature\", but I still think\nit future-proofs this section of code to handle anything.\n\n> +\t\tconst struct ref *ref = our_head_points_at;\n> +\t\tstruct object *o;\n> +\n> +\t\t/* Detached HEAD */\n> +\t\to = deref_tag(parse_object(ref->old_sha1), NULL, 0);\n> +\t\tupdate_ref(reflog_msg.buf, \"HEAD\", o->sha1, NULL,\n> +\t\t\t   REF_NODEREF, DIE_ON_ERR);\n\nIt's unlikely, but deref_tag can return NULL, in which case this will\nsegfault (ditto with parse_object, I think). I suspect that is a problem\nin lots of places, though. I wonder if deref_tag should simply die if we\nhave a missing object (and we can add a _gently form for things like\nfsck which want to handle the error condition).\n\nAlso, any reason the \"warn\" flag to deref_tag should not be 1?\n\n\nOther than those minor complaints, the patch looks good to me.\n\n-Peff\n"},{"id":"181984","messageId":"7v7h16nmpy.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1325771380-18862-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-05T16:22:17Z","receivedAt":"2012-01-05T16:22:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This allows you to do \"git clone --branch=v1.7.8 git.git\" and work\n> right away from there. No big deal, just one more convenient step, I\n> think. --branch taking a tag may be confusing though.\n>\n> We can still have master in this case instead of detached HEAD, which\n> may make more sense because we use --branch. I don't care much which\n> way should be used.\n\nYou clone a single lineage of the history, either shallowly or fully,\neither starting at the tip of one single branch or a named tag.\n\nWhat is the expected use scenario of a resulting repository of this new\nfeature? As this is creating a repository, not a tarball extract, you\ncertainly would want the user to build further history in the resulting\nrepository, and it would need a real branch at some point, preferably\nbefore any new commit is made. Which makes me think that the only reason\nwe would use a detached HEAD would be because we cannot decide what name\nto give to that single branch and make it the responsibility of the user\nto run \"git checkout -b $whatever\" as the first thing.\n\nI think the real cause of the above is because this patch and its previous\ncompanion patch conflate the meaning of the \"--branch\" option with the\npurpose of specifying which lineage of the history to copy. The option is\ndescribed to name the local branch that is checked out, instead of using\nthe the same name the remote's primary branch. But these patches abuse the\noption to name something different at the same time---the endpoint of the\nsingle lineage to be copied.\n\nThese two may often be the same, and use of \"clone --branch=master\" in\nsuch a case would mean that you want to name the local branch of the final\ncheckout to be \"master\" _and_ the endpoint of the single lineage you are\ncopying is also their \"master\".\n\nBut the \"tag\" extension proposed with this change is different.\n\nYou are specifying an endpoint of the single lineage with the option that\nis different from any of the branches at the origin, and because you used\nthe \"--branch\" option for that purpose, you lost the way to specify the\nprimary thing the option wanted to express: what the name of the resulting\ncheckout should be.\n\nPerhaps something like \"clone --branch=master --$endpoint=v1.7.8\" that\nsays \"I want a clone of the repository limited to a single lineage, whose\nhistory ends at the commit pointed by the v1.7.8 tag, and name the local\ncheckout my master branch\" be more appropriate?\n\nAlso, the user is likely to want to fetch and integrate from the origin\nwith his own history. How should \"git pull\" and \"git fetch\" work in the\nresulting repository? What should the remote.origin.* look like?\n"},{"id":"182010","messageId":"CACsJy8B2FGzQ2FFAx2wJohmkWe41+UETx6FZFwLNGY2fb74ZNg@mail.gmail.com","threadId":"29298","inReplyTo":"7v7h16nmpy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-06T07:35:21Z","receivedAt":"2012-01-06T07:35:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/5 Junio C Hamano <gitster@pobox.com>:\n> Also, the user is likely to want to fetch and integrate from the origin\n> with his own history. How should \"git pull\" and \"git fetch\" work in the\n> resulting repository? What should the remote.origin.* look like?\n\nfetch should behave the same regardless this patch. remote.origin.*\nlooks exactly the same. Branch tracking is not set up so pull works\nhalf way.\n\n> You clone a single lineage of the history, either shallowly or fully,\n> either starting at the tip of one single branch or a named tag.\n>\n> What is the expected use scenario of a resulting repository of this new\n> feature? As this is creating a repository, not a tarball extract, you\n> certainly would want the user to build further history in the resulting\n> repository, and it would need a real branch at some point, preferably\n> before any new commit is made. Which makes me think that the only reason\n> we would use a detached HEAD would be because we cannot decide what name\n> to give to that single branch and make it the responsibility of the user\n> to run \"git checkout -b $whatever\" as the first thing.\n\nWe can still commit with detached HEAD but I guess it's not\nrecommended. For a quick, throwaway repository, it's probably OK.\n\n> I think the real cause of the above is because this patch and its previous\n> companion patch conflate the meaning of the \"--branch\" option with the\n> purpose of specifying which lineage of the history to copy. The option is\n> described to name the local branch that is checked out, instead of using\n> the the same name the remote's primary branch. But these patches abuse the\n> option to name something different at the same time---the endpoint of the\n> single lineage to be copied.\n>\n> These two may often be the same, and use of \"clone --branch=master\" in\n> such a case would mean that you want to name the local branch of the final\n> checkout to be \"master\" _and_ the endpoint of the single lineage you are\n> copying is also their \"master\".\n>\n> But the \"tag\" extension proposed with this change is different.\n>\n> You are specifying an endpoint of the single lineage with the option that\n> is different from any of the branches at the origin, and because you used\n> the \"--branch\" option for that purpose, you lost the way to specify the\n> primary thing the option wanted to express: what the name of the resulting\n> checkout should be.\n>\n> Perhaps something like \"clone --branch=master --$endpoint=v1.7.8\" that\n> says \"I want a clone of the repository limited to a single lineage, whose\n> history ends at the commit pointed by the v1.7.8 tag, and name the local\n> checkout my master branch\" be more appropriate?\n\nBranch naming is a local thing. Maybe we could get away with always\nname it \"master\" if fetched from a tag. Branch tracking is not set up\nin this case, so if they want to follow up with upstream, they have to\ndo some more themselves (including renaming \"master\" to something else\nfor less confusion with upstream \"master\"). New option is not easily\ndiscovered, while \"git branch -r master foo\" is quite easy.\n-- \nDuy\n"},{"id":"182016","messageId":"CACsJy8BSzQiTScOhvQjLz8mQZC0g3fEOayBxn7n65t1tKwWnsw@mail.gmail.com","threadId":"29298","inReplyTo":"20120105141844.GA26771@sigill.intra.peff.net","subject":"Re: [PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-06T11:09:16Z","receivedAt":"2012-01-06T11:09:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/5 Jeff King <peff@peff.net>:\n>> @@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>                       strbuf_release(&head);\n>>\n>>                       if (!our_head_points_at) {\n>> +                             strbuf_addstr(&head, \"refs/tags/\");\n>> +                             strbuf_addstr(&head, option_branch);\n>> +                             our_head_points_at =\n>> +                                     find_ref_by_name(mapped_refs, head.buf);\n>> +                             strbuf_release(&head);\n>> +                     }\n>> +\n>> +                     if (!our_head_points_at) {\n>\n> Hmm. The context just above your patch that got snipped does this:\n>\n>    strbuf_addstr(&head, src_ref_prefix);\n>    strbuf_addstr(&head, option_branch);\n>    our_head_points_at =\n>        find_ref_by_name(mapped_refs, head.buf);\n>\n> where src_ref_prefix typically is \"refs/heads/\", and clearly you are\n> meaning to do the same thing for tags. But the use of \"src_ref_prefix\"\n> is interesting.\n>\n> It is always \"refs/heads/\" unless we are cloning into a bare mirror, in\n> which case it is \"refs/\". So with your patch in the non-mirror case,\n> doing \"--branch=foo\" would try \"refs/heads/foo\" followed by\n> \"refs/tags/foo\". Which makes sense. But in the mirror case, it will try\n> \"refs/foo\" followed by \"refs/tags/foo\", which is kind of odd.\n>\n> I wonder, though, if the original code makes any sense. By using\n> \"refs/\", I would have to say \"--branch=heads/foo\", which is kind of\n> weird and undocumented. I think it should probably always be\n> \"refs/heads/\", no matter if we are mirroring or not.\n\n--branch should not be used with --mirror in my opinion. --branch\nchanges HEAD so it's no longer an exact mirror.\n-- \nDuy\n"},{"id":"182024","messageId":"20120106144226.GA4525@sigill.intra.peff.net","threadId":"29298","inReplyTo":"CACsJy8BSzQiTScOhvQjLz8mQZC0g3fEOayBxn7n65t1tKwWnsw@mail.gmail.com","subject":"Re: [PATCH] clone: allow detached checkout when --branch takes a tag","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-01-06T14:42:27Z","receivedAt":"2012-01-06T14:42:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 06, 2012 at 06:09:16PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> > I wonder, though, if the original code makes any sense. By using\n> > \"refs/\", I would have to say \"--branch=heads/foo\", which is kind of\n> > weird and undocumented. I think it should probably always be\n> > \"refs/heads/\", no matter if we are mirroring or not.\n> \n> --branch should not be used with --mirror in my opinion. --branch\n> changes HEAD so it's no longer an exact mirror.\n\nYou could be making a repo that mirrors all of the refs, but has a\ndifferent HEAD (e.g., the upstream has \"development\" as the main branch,\nbut you want a local mirror with \"production\" as the HEAD).\n\nI agree it's an unlikely combination (which is probably why nobody has\ncomplained about the weird behavior), but I don't see a particular\nreason to forbid it.\n\n-Peff\n"},{"id":"182091","messageId":"1326023188-15559-1-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1325771380-18862-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 1/6] t5601: add &&","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:23Z","receivedAt":"2012-01-08T11:46:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5601-clone.sh |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 87ee016..49821eb 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -9,9 +9,9 @@ test_expect_success setup '\n \trm -fr .git &&\n \ttest_create_repo src &&\n \t(\n-\t\tcd src\n-\t\t>file\n-\t\tgit add file\n+\t\tcd src &&\n+\t\t>file &&\n+\t\tgit add file &&\n \t\tgit commit -m initial\n \t)\n \n-- \n1.7.8.36.g69ee2\n"},{"id":"182092","messageId":"1326023188-15559-2-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 2/6] clone: write detached HEAD in bare repositories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:24Z","receivedAt":"2012-01-08T11:46:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If we don't write, HEAD is still at refs/heads/master as initialized\nby init-db, which may or may not match remote's HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c  |    9 +++------\n t/t5601-clone.sh |   25 ++++++++++++++++++++++++-\n 2 files changed, 27 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 86db954..8dde1ea 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t}\n \t} else if (remote_head) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tif (!option_bare) {\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   remote_head->old_sha1,\n-\t\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\t\tour_head_points_at = remote_head;\n-\t\t}\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t\tour_head_points_at = remote_head;\n \t} else {\n \t\t/* Nothing to checkout out */\n \t\tif (!option_no_checkout)\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 49821eb..e0b8db6 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -12,7 +12,10 @@ test_expect_success setup '\n \t\tcd src &&\n \t\t>file &&\n \t\tgit add file &&\n-\t\tgit commit -m initial\n+\t\tgit commit -m initial &&\n+\t\techo 1 >file &&\n+\t\tgit add file &&\n+\t\tgit commit -m updated\n \t)\n \n '\n@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '\n \n '\n \n+test_expect_success 'clone --mirror with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --mirror src mirror.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=mirror.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n+test_expect_success 'clone --bare with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --bare src bare.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=bare.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n test_expect_success 'clone --bare names the local repository <name>.git' '\n \n \tgit clone --bare src &&\n-- \n1.7.8.36.g69ee2\n"},{"id":"182093","messageId":"1326023188-15559-3-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 3/6] clone: factor out checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:25Z","receivedAt":"2012-01-08T11:46:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Read HEAD from disk instead of relying on local variable\nour_head_points_at, so that if earlier code fails to make HEAD\nproperly, it'll be detected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 58 insertions(+), 43 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 8dde1ea..100056d 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static int checkout(void)\n+{\n+\tunsigned char sha1[20];\n+\tchar *head;\n+\tstruct lock_file *lock_file;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree *tree;\n+\tstruct tree_desc t;\n+\tint err = 0, fd;\n+\n+\tif (option_no_checkout)\n+\t\treturn 0;\n+\n+\thead = resolve_refdup(\"HEAD\", sha1, 1, NULL);\n+\tif (!head) {\n+\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n+\t\t\t  \"unable to checkout.\\n\"));\n+\t\treturn 0;\n+\t}\n+\tif (strcmp(head, \"HEAD\")) {\n+\t\tif (prefixcmp(head, \"refs/heads/\"))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t}\n+\tfree(head);\n+\n+\t/* We need to be in the new work tree for the checkout */\n+\tsetup_work_tree();\n+\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&opts, 0, sizeof opts);\n+\topts.update = 1;\n+\topts.merge = 1;\n+\topts.fn = oneway_merge;\n+\topts.verbose_update = (option_verbosity > 0);\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tunpack_trees(1, &t, &opts);\n+\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock_file))\n+\t\tdie(_(\"unable to write new index file\"));\n+\n+\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n+\t\t\tsha1_to_hex(sha1), \"1\", NULL);\n+\n+\tif (!err && option_recursive)\n+\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n+\n+\treturn err;\n+}\n+\n static int write_one_config(const char *key, const char *value, void *data)\n {\n \treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n \t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\tour_head_points_at = remote_head;\n-\t} else {\n-\t\t/* Nothing to checkout out */\n-\t\tif (!option_no_checkout)\n-\t\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n-\t\t\t\t\"unable to checkout.\\n\"));\n-\t\toption_no_checkout = 1;\n \t}\n \n \tif (transport) {\n@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_disconnect(transport);\n \t}\n \n-\tif (!option_no_checkout) {\n-\t\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n-\t\tstruct unpack_trees_options opts;\n-\t\tstruct tree *tree;\n-\t\tstruct tree_desc t;\n-\t\tint fd;\n-\n-\t\t/* We need to be in the new work tree for the checkout */\n-\t\tsetup_work_tree();\n-\n-\t\tfd = hold_locked_index(lock_file, 1);\n-\n-\t\tmemset(&opts, 0, sizeof opts);\n-\t\topts.update = 1;\n-\t\topts.merge = 1;\n-\t\topts.fn = oneway_merge;\n-\t\topts.verbose_update = (option_verbosity > 0);\n-\t\topts.src_index = &the_index;\n-\t\topts.dst_index = &the_index;\n-\n-\t\ttree = parse_tree_indirect(our_head_points_at->old_sha1);\n-\t\tparse_tree(tree);\n-\t\tinit_tree_desc(&t, tree->buffer, tree->size);\n-\t\tunpack_trees(1, &t, &opts);\n-\n-\t\tif (write_cache(fd, active_cache, active_nr) ||\n-\t\t    commit_locked_index(lock_file))\n-\t\t\tdie(_(\"unable to write new index file\"));\n-\n-\t\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n-\t\t\t\tsha1_to_hex(our_head_points_at->old_sha1), \"1\",\n-\t\t\t\tNULL);\n-\n-\t\tif (!err && option_recursive)\n-\t\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n-\t}\n+\terr = checkout();\n \n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_top);\n-- \n1.7.8.36.g69ee2\n"},{"id":"182094","messageId":"1326023188-15559-4-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:26Z","receivedAt":"2012-01-08T11:46:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It does not make sense to look outside refs/heads for HEAD's target\n(src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\ncode only allows symref in form refs/heads/...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |    5 +++--\n 1 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 100056d..914fd6b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -727,7 +727,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tif (option_branch) {\n \t\t\tstruct strbuf head = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&head, src_ref_prefix);\n+\t\t\tstrbuf_addstr(&head, \"refs/heads/\");\n \t\t\tstrbuf_addstr(&head, option_branch);\n \t\t\tour_head_points_at =\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n@@ -763,7 +763,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t      reflog_msg.buf);\n \t}\n \n-\tif (our_head_points_at) {\n+\tif (our_head_points_at &&\n+\t    !prefixcmp(our_head_points_at->name, \"refs/heads/\")) {\n \t\t/* Local default branch link */\n \t\tcreate_symref(\"HEAD\", our_head_points_at->name, NULL);\n \t\tif (!option_bare) {\n-- \n1.7.8.36.g69ee2\n"},{"id":"182096","messageId":"1326023188-15559-5-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 5/6] clone: allow --branch to take a tag","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:27Z","receivedAt":"2012-01-08T11:46:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Because a tag ref cannot be put to HEAD, HEAD will become detached.\nThis is consistent with \"git checkout <tag>\".\n\nThis is mostly useful in shallow clone, where it allows you to clone a\ntag in addtion to branches.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Although 'git clone --depth 1 --branch <tag>' still needs fixups on\n top. I'll do that later.\n\n Documentation/git-clone.txt |    5 +++--\n builtin/clone.c             |   14 ++++++++++++++\n t/t5601-clone.sh            |    9 +++++++++\n 3 files changed, 26 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 4b8b26b..db2b29c 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -146,8 +146,9 @@ 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. In a non-bare repository, this is the branch that will\n-\tbe checked out.\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 \n --upload-pack <upload-pack>::\n -u <upload-pack>::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 914fd6b..0d4b9ab 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -734,6 +734,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tstrbuf_release(&head);\n \n \t\t\tif (!our_head_points_at) {\n+\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n+\t\t\t\tstrbuf_addstr(&head, option_branch);\n+\t\t\t\tour_head_points_at =\n+\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n+\t\t\t\tstrbuf_release(&head);\n+\t\t\t}\n+\n+\t\t\tif (!our_head_points_at) {\n \t\t\t\twarning(_(\"Remote branch %s not found in \"\n \t\t\t\t\t\"upstream %s, using HEAD instead\"),\n \t\t\t\t\toption_branch, option_origin);\n@@ -776,6 +784,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tinstall_branch_config(0, head, option_origin,\n \t\t\t\t\t      our_head_points_at->name);\n \t\t}\n+\t} else if (our_head_points_at) {\n+\t\tconst struct ref *ref = our_head_points_at;\n+\t\tstruct commit *c = lookup_commit_reference(ref->old_sha1);\n+\t\t/* Source had detached HEAD pointing somewhere. */\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", c->object.sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n \t} else if (remote_head) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e0b8db6..67869b4 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '\n \tgrep /src/\\\\.git/objects target-10/objects/info/alternates\n '\n \n+test_expect_success 'clone checking out a tag' '\n+\tgit clone --branch=some-tag src dst.tag &&\n+\tGIT_DIR=src/.git git rev-parse some-tag >expected &&\n+\ttest_cmp expected dst.tag/.git/HEAD &&\n+\tGIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" >fetch.expected &&\n+\ttest_cmp fetch.expected fetch.actual\n+'\n+\n test_done\n-- \n1.7.8.36.g69ee2\n"},{"id":"182095","messageId":"1326023188-15559-6-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 6/6] clone: print advice on checking out detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-08T11:46:28Z","receivedAt":"2012-01-08T11:46:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c           |   14 ++++++++++++++\n advice.h           |    1 +\n builtin/checkout.c |   16 +---------------\n builtin/clone.c    |    5 ++++-\n 4 files changed, 20 insertions(+), 16 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex e02e632..3e1a145 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)\n \terror_resolve_conflict(me);\n \tdie(\"Exiting because of an unresolved conflict.\");\n }\n+\n+void detach_advice(const char *new_name)\n+{\n+\tconst char fmt[] =\n+\t\"Note: checking out '%s'.\\n\\n\"\n+\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n+\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n+\t\"  git checkout -b new_branch_name\\n\\n\";\n+\n+\tfprintf(stderr, fmt, new_name);\n+}\ndiff --git a/advice.h b/advice.h\nindex e5d0af7..7bda45b 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);\n void advise(const char *advice, ...);\n int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n+void detach_advice(const char *new_name);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f1984d9..5bf96ba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n-static void detach_advice(const char *old_path, const char *new_name)\n-{\n-\tconst char fmt[] =\n-\t\"Note: checking out '%s'.\\n\\n\"\n-\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n-\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n-\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b new_branch_name\\n\\n\";\n-\n-\tfprintf(stderr, fmt, new_name);\n-}\n-\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n \t\t\tif (old->path && advice_detached_head)\n-\t\t\t\tdetach_advice(old->path, new->name);\n+\t\t\t\tdetach_advice(new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n \t} else if (new->path) {\t/* Switch branches. */\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 0d4b9ab..52596db 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -467,7 +467,10 @@ static int checkout(void)\n \t\t\t  \"unable to checkout.\\n\"));\n \t\treturn 0;\n \t}\n-\tif (strcmp(head, \"HEAD\")) {\n+\tif (!strcmp(head, \"HEAD\")) {\n+\t\tif (advice_detached_head)\n+\t\t\tdetach_advice(sha1_to_hex(sha1));\n+\t} else {\n \t\tif (prefixcmp(head, \"refs/heads/\"))\n \t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n \t}\n-- \n1.7.8.36.g69ee2\n"},{"id":"182192","messageId":"7vd3ascs85.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326023188-15559-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 3/6] clone: factor out checkout code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-10T00:32:26Z","receivedAt":"2012-01-10T00:32:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Read HEAD from disk instead of relying on local variable\n> our_head_points_at, so that if earlier code fails to make HEAD\n> properly, it'll be detected.\n\nThe end result might be more or less the same with your patch from the\nend-user's point of view, but \"if earlier code fails\", shouldn't you\ndetect and diagnose it right there?\n\nIf you observe lack of \"HEAD\" in checkout(), you cannot tell if that was\nbecause the remote did not have anything usable in the first place, or\nbecause we knew where it should point at (and may have even attempted to\ncreate it) but somehow failed to make it point at it.\n"},{"id":"182193","messageId":"7v8vlgcs5n.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326023188-15559-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-10T00:33:56Z","receivedAt":"2012-01-10T00:33:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Up to this point I find the series makes sense.\n"},{"id":"182194","messageId":"7v4nw4cs1x.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326023188-15559-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-10T00:36:10Z","receivedAt":"2012-01-10T00:36:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This patch makes 100% sense _if_ we let clone result in a repository with\na detached HEAD, which I am not sure if it is a good idea, or if it is\nbetter to fail the attempt to clone to give incentive to the owner of the\nremote repository to fix it.\n"},{"id":"182198","messageId":"CACsJy8CuYkzFVrEG6T2HUAwJGnjit2xWt3VSN-9USt7h+B_CBw@mail.gmail.com","threadId":"29298","inReplyTo":"7v4nw4cs1x.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T01:54:22Z","receivedAt":"2012-01-10T01:54:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/10 Junio C Hamano <gitster@pobox.com>:\n> This patch makes 100% sense _if_ we let clone result in a repository with\n> a detached HEAD, which I am not sure if it is a good idea, or if it is\n> better to fail the attempt to clone to give incentive to the owner of the\n> remote repository to fix it.\n\nThen a hostile remote can stop users from cloning his repository by\ndetaching HEAD? That's not nice. On the other hand, if specifying\n--branch=<wrong-branch> leads to detached case, then we should\nprobably refuse to clone. But that should happen before transferring\nthe pack.\n-- \nDuy\n"},{"id":"182199","messageId":"CACsJy8DZpA0sQ6ZYjgrp8PsRTsYm0nOfSXcDOEhB2TRjqwbM0Q@mail.gmail.com","threadId":"29298","inReplyTo":"7vd3ascs85.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 3/6] clone: factor out checkout code","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T02:01:18Z","receivedAt":"2012-01-10T02:01:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/10 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Read HEAD from disk instead of relying on local variable\n>> our_head_points_at, so that if earlier code fails to make HEAD\n>> properly, it'll be detected.\n>\n> The end result might be more or less the same with your patch from the\n> end-user's point of view, but \"if earlier code fails\", shouldn't you\n> detect and diagnose it right there?\n\nSure, but another fence does not harm. There's also one thing I missed\nin the commit message that it makes update head code and checkout code\nmore independent. Update head code does not need to maintain\nour_head_points_at at the end for checkout anymore.\n\n> If you observe lack of \"HEAD\" in checkout(), you cannot tell if that was\n> because the remote did not have anything usable in the first place, or\n> because we knew where it should point at (and may have even attempted to\n> create it) but somehow failed to make it point at it.\n\nThe lack of HEAD probably won't happen because HEAD is created by\ndefault in init-db. This is mainly to catch invalid HEAD (like putting\n\"refs/tags/something\" in HEAD).\n-- \nDuy\n"},{"id":"182205","messageId":"7vlipgb1r1.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"CACsJy8CuYkzFVrEG6T2HUAwJGnjit2xWt3VSN-9USt7h+B_CBw@mail.gmail.com","subject":"Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-10T04:49:38Z","receivedAt":"2012-01-10T04:49:38Z","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> 2012/1/10 Junio C Hamano <gitster@pobox.com>:\n>> This patch makes 100% sense _if_ we let clone result in a repository with\n>> a detached HEAD, which I am not sure if it is a good idea, or if it is\n>> better to fail the attempt to clone to give incentive to the owner of the\n>> remote repository to fix it.\n>\n> Then a hostile remote can stop users from cloning his repository by\n> detaching HEAD? That's not nice.\n\nThat's crazy talk. Why does anybody from a hostile remote to begin with?\n\n> On the other hand, if specifying --branch=<wrong-branch> leads to\n> detached case, then we should probably refuse to clone.\n\nIf you mean \"nonexistent\" by \"wrong\", yeah, I agree, as we do not know\nwhat the user asked us to pull down in that case.\n"},{"id":"182207","messageId":"7vhb04b1bc.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"CACsJy8DZpA0sQ6ZYjgrp8PsRTsYm0nOfSXcDOEhB2TRjqwbM0Q@mail.gmail.com","subject":"Re: [PATCH v2 3/6] clone: factor out checkout code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-10T04:59:03Z","receivedAt":"2012-01-10T04:59:03Z","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> 2012/1/10 Junio C Hamano <gitster@pobox.com>:\n>>\n>>> Read HEAD from disk instead of relying on local variable\n>>> our_head_points_at, so that if earlier code fails to make HEAD\n>>> properly, it'll be detected.\n>>\n>> The end result might be more or less the same with your patch from the\n>> end-user's point of view, but \"if earlier code fails\", shouldn't you\n>> detect and diagnose it right there?\n>\n> Sure, but another fence does not harm.\n\nBut that is not \"another\" fence but is the _only_ fence, as you do not\ncheck after running update_ref of \"HEAD\".\n\n> There's also one thing I missed in the commit message that it makes\n> update head code and checkout code more independent. Update head code\n> does not need to maintain our_head_points_at at the end for checkout\n> anymore.\n\nI like that reasoning in general. The logic ought to be:\n\n - Learn what the remote has;\n\n - Combine it with --branch parameter, determine what local branch our\n   head _should_ point at;\n\n - Make our head point at it, and check it out.\n\nI wonder if we can somehow make the above logic more clear in the\ncode. Perhaps the first two could be made into a single helper function\n\"decide_local_branch()\", and the third would be the \"checkout()\" function\nin your patch, updated to take \"const char *\" parameter or something?\n\n> The lack of HEAD probably won't happen because HEAD is created by\n> default in init-db. This is mainly to catch invalid HEAD (like putting\n> \"refs/tags/something\" in HEAD).\n\nSorry; what I meant by \"lack\" was \"... if earlier code fails to make HEAD\nproperly\" case.\n"},{"id":"182210","messageId":"CACsJy8D=eVfhZOC3m3H50WZxa_Ep_UVYD_ZaEVj8H1C1z7+PRQ@mail.gmail.com","threadId":"29298","inReplyTo":"7vlipgb1r1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T05:54:11Z","receivedAt":"2012-01-10T05:54:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jan 10, 2012 at 11:49 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> 2012/1/10 Junio C Hamano <gitster@pobox.com>:\n>>> This patch makes 100% sense _if_ we let clone result in a repository with\n>>> a detached HEAD, which I am not sure if it is a good idea, or if it is\n>>> better to fail the attempt to clone to give incentive to the owner of the\n>>> remote repository to fix it.\n>>\n>> Then a hostile remote can stop users from cloning his repository by\n>> detaching HEAD? That's not nice.\n>\n> That's crazy talk. Why does anybody from a hostile remote to begin with?\n\nThe point is, why punish client for remote's problems? If I have to\ntalk to upstream and wait for them to fix their repository, I might as\nwell give up cloning and move on. It's OK to annoy users to the point\nthat they ask upstream for a fix, but we should not disallow clone in\nthat case.\n-- \nDuy\n"},{"id":"182212","messageId":"CACsJy8DG2CCbDipfFEp2JM5wb0Nn9F7v8Cxx1bPAZ-40OdEA9A@mail.gmail.com","threadId":"29298","inReplyTo":"7vhb04b1bc.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 3/6] clone: factor out checkout code","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T05:57:23Z","receivedAt":"2012-01-10T05:57:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jan 10, 2012 at 11:59 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> There's also one thing I missed in the commit message that it makes\n>> update head code and checkout code more independent. Update head code\n>> does not need to maintain our_head_points_at at the end for checkout\n>> anymore.\n>\n> I like that reasoning in general. The logic ought to be:\n>\n>  - Learn what the remote has;\n>\n>  - Combine it with --branch parameter, determine what local branch our\n>   head _should_ point at;\n>\n>  - Make our head point at it, and check it out.\n>\n> I wonder if we can somehow make the above logic more clear in the\n> code. Perhaps the first two could be made into a single helper function\n> \"decide_local_branch()\", and the third would be the \"checkout()\" function\n> in your patch, updated to take \"const char *\" parameter or something?\n\nyeah, I split the first two into update_head() but dropped it for some\nreasons I don't remember. That would make the main function easier to\nfollow. I'll look at it again.\n-- \nDuy\n"},{"id":"182221","messageId":"1326189427-20800-1-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 00/10] nd/clone-detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:56:57Z","receivedAt":"2012-01-10T09:56:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Compare to v2, this round does more refactoring, which makes\ncmd_clone() looks easier to follow in the end, in my opinion.\n\nThere's also 7/10 that refuses --branch=<nonexistent>. I don't know if\nI react too strong. The current behavior is fall back to remote's HEAD\n(and detached HEAD if remote's HEAD is detached). Maybe we should only\nrefuse it when it leads to detached HEAD and let it fall back to\nremote's HEAD otherwise.\n\nThe last two patches remain debatable. If we disallow detached HEAD\nfrom new clones, perhaps we could put <tag>^{commit} to\nrefs/heads/master then drop the last patch. t3501.6, t5527.2, t5707.5,\nt7406.29 likes to have detached HEAD, but those can be fixed.\n\nNguyễn Thái Ngọc Duy (10):\n  t5601: add missing && cascade\n  clone: write detached HEAD in bare repositories\n  clone: factor out checkout code\n  clone: factor out HEAD update code\n  clone: factor out remote ref writing\n  clone: delay cloning until after remote HEAD checking\n  clone: --branch=<branch> always means refs/heads/<branch>\n  clone: refuse to clone if --branch points to bogus ref\n  clone: allow --branch to take a tag\n  clone: print advice on checking out detached HEAD\n\n Documentation/git-clone.txt |    5 +-\n advice.c                    |   14 +++\n advice.h                    |    1 +\n builtin/checkout.c          |   16 +---\n builtin/clone.c             |  252 +++++++++++++++++++++++++------------------\n t/t5601-clone.sh            |   40 ++++++-\n t/t5706-clone-branch.sh     |    8 +-\n transport.c                 |    5 +-\n 8 files changed, 207 insertions(+), 134 deletions(-)\n\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182222","messageId":"1326189427-20800-2-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 01/10] t5601: add missing && cascade","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:56:58Z","receivedAt":"2012-01-10T09:56:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5601-clone.sh |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 87ee016..49821eb 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -9,9 +9,9 @@ test_expect_success setup '\n \trm -fr .git &&\n \ttest_create_repo src &&\n \t(\n-\t\tcd src\n-\t\t>file\n-\t\tgit add file\n+\t\tcd src &&\n+\t\t>file &&\n+\t\tgit add file &&\n \t\tgit commit -m initial\n \t)\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182223","messageId":"1326189427-20800-3-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 02/10] clone: write detached HEAD in bare repositories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:56:59Z","receivedAt":"2012-01-10T09:56:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If we don't write, HEAD is still at refs/heads/master as initialized\nby init-db, which may or may not match remote's HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c  |    9 +++------\n t/t5601-clone.sh |   25 ++++++++++++++++++++++++-\n 2 files changed, 27 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 86db954..8dde1ea 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t}\n \t} else if (remote_head) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tif (!option_bare) {\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   remote_head->old_sha1,\n-\t\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\t\tour_head_points_at = remote_head;\n-\t\t}\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t\tour_head_points_at = remote_head;\n \t} else {\n \t\t/* Nothing to checkout out */\n \t\tif (!option_no_checkout)\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 49821eb..e0b8db6 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -12,7 +12,10 @@ test_expect_success setup '\n \t\tcd src &&\n \t\t>file &&\n \t\tgit add file &&\n-\t\tgit commit -m initial\n+\t\tgit commit -m initial &&\n+\t\techo 1 >file &&\n+\t\tgit add file &&\n+\t\tgit commit -m updated\n \t)\n \n '\n@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '\n \n '\n \n+test_expect_success 'clone --mirror with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --mirror src mirror.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=mirror.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n+test_expect_success 'clone --bare with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --bare src bare.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=bare.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n test_expect_success 'clone --bare names the local repository <name>.git' '\n \n \tgit clone --bare src &&\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182224","messageId":"1326189427-20800-4-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 03/10] clone: factor out checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:00Z","receivedAt":"2012-01-10T09:57:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Read HEAD from disk instead of relying on local variable\nour_head_points_at. This reduces complexity of cmd_clone() a little\nbit.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 58 insertions(+), 43 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 8dde1ea..100056d 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static int checkout(void)\n+{\n+\tunsigned char sha1[20];\n+\tchar *head;\n+\tstruct lock_file *lock_file;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree *tree;\n+\tstruct tree_desc t;\n+\tint err = 0, fd;\n+\n+\tif (option_no_checkout)\n+\t\treturn 0;\n+\n+\thead = resolve_refdup(\"HEAD\", sha1, 1, NULL);\n+\tif (!head) {\n+\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n+\t\t\t  \"unable to checkout.\\n\"));\n+\t\treturn 0;\n+\t}\n+\tif (strcmp(head, \"HEAD\")) {\n+\t\tif (prefixcmp(head, \"refs/heads/\"))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t}\n+\tfree(head);\n+\n+\t/* We need to be in the new work tree for the checkout */\n+\tsetup_work_tree();\n+\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&opts, 0, sizeof opts);\n+\topts.update = 1;\n+\topts.merge = 1;\n+\topts.fn = oneway_merge;\n+\topts.verbose_update = (option_verbosity > 0);\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tunpack_trees(1, &t, &opts);\n+\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock_file))\n+\t\tdie(_(\"unable to write new index file\"));\n+\n+\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n+\t\t\tsha1_to_hex(sha1), \"1\", NULL);\n+\n+\tif (!err && option_recursive)\n+\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n+\n+\treturn err;\n+}\n+\n static int write_one_config(const char *key, const char *value, void *data)\n {\n \treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n \t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\tour_head_points_at = remote_head;\n-\t} else {\n-\t\t/* Nothing to checkout out */\n-\t\tif (!option_no_checkout)\n-\t\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n-\t\t\t\t\"unable to checkout.\\n\"));\n-\t\toption_no_checkout = 1;\n \t}\n \n \tif (transport) {\n@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_disconnect(transport);\n \t}\n \n-\tif (!option_no_checkout) {\n-\t\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n-\t\tstruct unpack_trees_options opts;\n-\t\tstruct tree *tree;\n-\t\tstruct tree_desc t;\n-\t\tint fd;\n-\n-\t\t/* We need to be in the new work tree for the checkout */\n-\t\tsetup_work_tree();\n-\n-\t\tfd = hold_locked_index(lock_file, 1);\n-\n-\t\tmemset(&opts, 0, sizeof opts);\n-\t\topts.update = 1;\n-\t\topts.merge = 1;\n-\t\topts.fn = oneway_merge;\n-\t\topts.verbose_update = (option_verbosity > 0);\n-\t\topts.src_index = &the_index;\n-\t\topts.dst_index = &the_index;\n-\n-\t\ttree = parse_tree_indirect(our_head_points_at->old_sha1);\n-\t\tparse_tree(tree);\n-\t\tinit_tree_desc(&t, tree->buffer, tree->size);\n-\t\tunpack_trees(1, &t, &opts);\n-\n-\t\tif (write_cache(fd, active_cache, active_nr) ||\n-\t\t    commit_locked_index(lock_file))\n-\t\t\tdie(_(\"unable to write new index file\"));\n-\n-\t\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n-\t\t\t\tsha1_to_hex(our_head_points_at->old_sha1), \"1\",\n-\t\t\t\tNULL);\n-\n-\t\tif (!err && option_recursive)\n-\t\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n-\t}\n+\terr = checkout();\n \n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_top);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182225","messageId":"1326189427-20800-5-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 04/10] clone: factor out HEAD update code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:01Z","receivedAt":"2012-01-10T09:57:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   36 +++++++++++++++++++-----------------\n 1 files changed, 19 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 100056d..26a037c 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,24 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static void update_head(const struct ref *our, const struct ref *remote,\n+\t\t\tconst char *msg)\n+{\n+\tif (our) {\n+\t\t/* Local default branch link */\n+\t\tcreate_symref(\"HEAD\", our->name, NULL);\n+\t\tif (!option_bare) {\n+\t\t\tconst char *head = skip_prefix(our->name, \"refs/heads/\");\n+\t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n+\t\t\tinstall_branch_config(0, head, option_origin, our->name);\n+\t\t}\n+\t} else if (remote) {\n+\t\t/* Source had detached HEAD pointing somewhere. */\n+\t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t}\n+}\n+\n static int checkout(void)\n {\n \tunsigned char sha1[20];\n@@ -763,23 +781,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t      reflog_msg.buf);\n \t}\n \n-\tif (our_head_points_at) {\n-\t\t/* Local default branch link */\n-\t\tcreate_symref(\"HEAD\", our_head_points_at->name, NULL);\n-\t\tif (!option_bare) {\n-\t\t\tconst char *head = skip_prefix(our_head_points_at->name,\n-\t\t\t\t\t\t       \"refs/heads/\");\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   our_head_points_at->old_sha1,\n-\t\t\t\t   NULL, 0, DIE_ON_ERR);\n-\t\t\tinstall_branch_config(0, head, option_origin,\n-\t\t\t\t\t      our_head_points_at->name);\n-\t\t}\n-\t} else if (remote_head) {\n-\t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n-\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t}\n+\tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n \tif (transport) {\n \t\ttransport_unlock_pack(transport);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182226","messageId":"1326189427-20800-6-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 05/10] clone: factor out remote ref writing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:02Z","receivedAt":"2012-01-10T09:57:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   36 ++++++++++++++++++++++++------------\n 1 files changed, 24 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 26a037c..73d07ed 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,28 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static void update_remote_refs(const struct ref *refs,\n+\t\t\t       const struct ref *mapped_refs,\n+\t\t\t       const struct ref *remote_head_points_at,\n+\t\t\t       const char *branch_top,\n+\t\t\t       const char *msg)\n+{\n+\tif (refs) {\n+\t\tclear_extra_refs();\n+\t\twrite_remote_refs(mapped_refs);\n+\t}\n+\n+\tif (remote_head_points_at && !option_bare) {\n+\t\tstruct strbuf head_ref = STRBUF_INIT;\n+\t\tstrbuf_addstr(&head_ref, branch_top);\n+\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n+\t\tcreate_symref(head_ref.buf,\n+\t\t\t      remote_head_points_at->peer_ref->name,\n+\t\t\t      msg);\n+\t}\n+\n+}\n+\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n@@ -735,10 +757,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (refs) {\n-\t\tclear_extra_refs();\n-\n-\t\twrite_remote_refs(mapped_refs);\n-\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n \t\t\tguess_remote_head(remote_head, mapped_refs, 0);\n@@ -772,14 +790,8 @@ 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 (remote_head_points_at && !option_bare) {\n-\t\tstruct strbuf head_ref = STRBUF_INIT;\n-\t\tstrbuf_addstr(&head_ref, branch_top.buf);\n-\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n-\t\tcreate_symref(head_ref.buf,\n-\t\t\t      remote_head_points_at->peer_ref->name,\n-\t\t\t      reflog_msg.buf);\n-\t}\n+\tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n+\t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182227","messageId":"1326189427-20800-7-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:03Z","receivedAt":"2012-01-10T09:57:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This gives us an opportunity to abort the command during remote HEAD\ncheck without wasting much bandwidth.\n\nCloning with remote-helper remains before the check because the remote\nhelper updates mapped_refs, which is necessary for remote ref checks.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n I'm not familiar with remote-helper to see if there's any better way\n to do this..\n\n builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------\n transport.c     |    5 ++++-\n 2 files changed, 31 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 73d07ed..05224d7 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -361,13 +361,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,\n \tclosedir(dir);\n }\n \n-static const struct ref *clone_local(const char *src_repo,\n-\t\t\t\t     const char *dest_repo)\n+static void clone_local(const char *src_repo, const char *dest_repo)\n {\n-\tconst struct ref *ret;\n-\tstruct remote *remote;\n-\tstruct transport *transport;\n-\n \tif (option_shared) {\n \t\tstruct strbuf alt = STRBUF_INIT;\n \t\tstrbuf_addf(&alt, \"%s/objects\", src_repo);\n@@ -383,13 +378,8 @@ static const struct ref *clone_local(const char *src_repo,\n \t\tstrbuf_release(&dest);\n \t}\n \n-\tremote = remote_get(src_repo);\n-\ttransport = transport_get(remote, src_repo);\n-\tret = transport_get_remote_refs(transport);\n-\ttransport_disconnect(transport);\n \tif (0 <= option_verbosity)\n \t\tprintf(_(\"done.\\n\"));\n-\treturn ret;\n }\n \n static const char *junk_work_tree;\n@@ -576,6 +566,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tchar *src_ref_prefix = \"refs/heads/\";\n+\tstruct remote *remote;\n \tint err = 0;\n \n \tstruct refspec *refspec;\n@@ -727,13 +718,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_reset(&value);\n \n-\tif (is_local) {\n-\t\trefs = clone_local(path, git_dir);\n-\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t} else {\n-\t\tstruct remote *remote = remote_get(option_origin);\n-\t\ttransport = transport_get(remote, remote->url[0]);\n+\tremote = remote_get(option_origin);\n+\ttransport = transport_get(remote, remote->url[0]);\n \n+\tif (!is_local) {\n \t\tif (!transport->get_refs_list || !transport->fetch)\n \t\t\tdie(_(\"Don't know how to clone %s\"), transport->url);\n \n@@ -748,14 +736,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (option_upload_pack)\n \t\t\ttransport_set_option(transport, TRANS_OPT_UPLOADPACK,\n \t\t\t\t\t     option_upload_pack);\n-\n-\t\trefs = transport_get_remote_refs(transport);\n-\t\tif (refs) {\n-\t\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t\t\ttransport_fetch_refs(transport, mapped_refs);\n-\t\t}\n \t}\n \n+\trefs = transport_get_remote_refs(transport);\n+\tmapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;\n+\n+\t/*\n+\t * mapped_refs may be updated if transport-helper is used so\n+\t * we need fetch it early because remote_head code below\n+\t * relies on it.\n+\t *\n+\t * for normal clones, transport_get_remote_refs() should\n+\t * return reliable ref set, we can delay cloning until after\n+\t * remote HEAD check.\n+\t */\n+\tif (!is_local && remote->foreign_vcs && refs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tif (refs) {\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n@@ -790,15 +787,18 @@ 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 (is_local)\n+\t\tclone_local(path, git_dir);\n+\telse if (refs && !remote->foreign_vcs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n \t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-\tif (transport) {\n-\t\ttransport_unlock_pack(transport);\n-\t\ttransport_disconnect(transport);\n-\t}\n+\ttransport_unlock_pack(transport);\n+\ttransport_disconnect(transport);\n \n \terr = checkout();\n \ndiff --git a/transport.c b/transport.c\nindex a99b7c9..aae9889 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \t\twhile (is_urlschemechar(p == url, *p))\n \t\t\tp++;\n-\t\tif (!prefixcmp(p, \"::\"))\n+\t\tif (!prefixcmp(p, \"::\")) {\n \t\t\thelper = xstrndup(url, p - url);\n+\t\t\tremote->foreign_vcs = helper;\n+\t\t}\n \t}\n \n \tif (helper) {\n@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\tchar *handler = xmalloc(len + 1);\n \t\thandler[len] = 0;\n \t\tstrncpy(handler, url, len);\n+\t\tremote->foreign_vcs = helper;\n \t\ttransport_helper_init(ret, handler);\n \t}\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182228","messageId":"1326189427-20800-8-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:04Z","receivedAt":"2012-01-10T09:57:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It does not make sense to look outside refs/heads for HEAD's target\n(src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\ncode only allows symref in form refs/heads/...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 05224d7..960242f 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n-\tif (our) {\n+\tif (our && !prefixcmp(our->name, \"refs/heads/\")) {\n \t\t/* Local default branch link */\n \t\tcreate_symref(\"HEAD\", our->name, NULL);\n \t\tif (!option_bare) {\n@@ -760,7 +760,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tif (option_branch) {\n \t\t\tstruct strbuf head = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&head, src_ref_prefix);\n+\t\t\tstrbuf_addstr(&head, \"refs/heads/\");\n \t\t\tstrbuf_addstr(&head, option_branch);\n \t\t\tour_head_points_at =\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182229","messageId":"1326189427-20800-9-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 08/10] clone: refuse to clone if --branch points to bogus ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:05Z","receivedAt":"2012-01-10T09:57:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's possible that users make a typo in the branch name. Stop and let\nusers recheck. Falling back to remote's HEAD is not documented any\nway.\n\nExcept when using remote helper, the pack has not been transferred at\nthis stage yet so we don't waste much bandwidth.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c         |   10 ++++------\n t/t5706-clone-branch.sh |    8 ++------\n 2 files changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 960242f..f751997 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -766,12 +766,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n \t\t\tstrbuf_release(&head);\n \n-\t\t\tif (!our_head_points_at) {\n-\t\t\t\twarning(_(\"Remote branch %s not found in \"\n-\t\t\t\t\t\"upstream %s, using HEAD instead\"),\n-\t\t\t\t\toption_branch, option_origin);\n-\t\t\t\tour_head_points_at = remote_head_points_at;\n-\t\t\t}\n+\t\t\tif (!our_head_points_at)\n+\t\t\t\tdie(_(\"Remote branch %s not found in \"\n+\t\t\t\t      \"upstream %s, using HEAD instead\"),\n+\t\t\t\t    option_branch, option_origin);\n \t\t}\n \t\telse\n \t\t\tour_head_points_at = remote_head_points_at;\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nindex f3f9a76..56be67e 100755\n--- a/t/t5706-clone-branch.sh\n+++ b/t/t5706-clone-branch.sh\n@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '\n \t)\n '\n \n-test_expect_success 'clone -b with bogus branch chooses HEAD' '\n-\tgit clone -b bogus parent clone-bogus &&\n-\t(cd clone-bogus &&\n-\t check_HEAD master &&\n-\t check_file one\n-\t)\n+test_expect_success 'clone -b with bogus branch' '\n+\ttest_must_fail git clone -b bogus parent clone-bogus\n '\n \n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182230","messageId":"1326189427-20800-10-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 09/10] clone: allow --branch to take a tag","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:06Z","receivedAt":"2012-01-10T09:57:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Because a tag ref cannot be put to HEAD, HEAD will become detached.\nThis is consistent with \"git checkout <tag>\".\n\nThis is mostly useful in shallow clone, where it allows you to clone a\ntag in addtion to branches.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |    5 +++--\n builtin/clone.c             |   13 +++++++++++++\n t/t5601-clone.sh            |    9 +++++++++\n 3 files changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 4b8b26b..db2b29c 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -146,8 +146,9 @@ 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. In a non-bare repository, this is the branch that will\n-\tbe checked out.\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 \n --upload-pack <upload-pack>::\n -u <upload-pack>::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex f751997..ed18c1a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -471,6 +471,11 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n \t\t\tinstall_branch_config(0, head, option_origin, our->name);\n \t\t}\n+\t} else if (our) {\n+\t\tstruct commit *c = lookup_commit_reference(our->old_sha1);\n+\t\t/* Source had detached HEAD pointing somewhere. */\n+\t\tupdate_ref(msg, \"HEAD\", c->object.sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n \t} else if (remote) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n@@ -766,6 +771,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n \t\t\tstrbuf_release(&head);\n \n+\t\t\tif (!our_head_points_at) {\n+\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n+\t\t\t\tstrbuf_addstr(&head, option_branch);\n+\t\t\t\tour_head_points_at =\n+\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n+\t\t\t\tstrbuf_release(&head);\n+\t\t\t}\n+\n \t\t\tif (!our_head_points_at)\n \t\t\t\tdie(_(\"Remote branch %s not found in \"\n \t\t\t\t      \"upstream %s, using HEAD instead\"),\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e0b8db6..67869b4 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '\n \tgrep /src/\\\\.git/objects target-10/objects/info/alternates\n '\n \n+test_expect_success 'clone checking out a tag' '\n+\tgit clone --branch=some-tag src dst.tag &&\n+\tGIT_DIR=src/.git git rev-parse some-tag >expected &&\n+\ttest_cmp expected dst.tag/.git/HEAD &&\n+\tGIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" >fetch.expected &&\n+\ttest_cmp fetch.expected fetch.actual\n+'\n+\n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182231","messageId":"1326189427-20800-11-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326023188-15559-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 10/10] clone: print advice on checking out detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-10T09:57:07Z","receivedAt":"2012-01-10T09:57:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c           |   14 ++++++++++++++\n advice.h           |    1 +\n builtin/checkout.c |   16 +---------------\n builtin/clone.c    |    5 ++++-\n 4 files changed, 20 insertions(+), 16 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex e02e632..3e1a145 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)\n \terror_resolve_conflict(me);\n \tdie(\"Exiting because of an unresolved conflict.\");\n }\n+\n+void detach_advice(const char *new_name)\n+{\n+\tconst char fmt[] =\n+\t\"Note: checking out '%s'.\\n\\n\"\n+\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n+\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n+\t\"  git checkout -b new_branch_name\\n\\n\";\n+\n+\tfprintf(stderr, fmt, new_name);\n+}\ndiff --git a/advice.h b/advice.h\nindex e5d0af7..7bda45b 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);\n void advise(const char *advice, ...);\n int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n+void detach_advice(const char *new_name);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f1984d9..5bf96ba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n-static void detach_advice(const char *old_path, const char *new_name)\n-{\n-\tconst char fmt[] =\n-\t\"Note: checking out '%s'.\\n\\n\"\n-\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n-\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n-\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b new_branch_name\\n\\n\";\n-\n-\tfprintf(stderr, fmt, new_name);\n-}\n-\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n \t\t\tif (old->path && advice_detached_head)\n-\t\t\t\tdetach_advice(old->path, new->name);\n+\t\t\t\tdetach_advice(new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n \t} else if (new->path) {\t/* Switch branches. */\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex ed18c1a..70f0280 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -502,7 +502,10 @@ static int checkout(void)\n \t\t\t  \"unable to checkout.\\n\"));\n \t\treturn 0;\n \t}\n-\tif (strcmp(head, \"HEAD\")) {\n+\tif (!strcmp(head, \"HEAD\")) {\n+\t\tif (advice_detached_head)\n+\t\t\tdetach_advice(sha1_to_hex(sha1));\n+\t} else {\n \t\tif (prefixcmp(head, \"refs/heads/\"))\n \t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n \t}\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182370","messageId":"7v8vle6ng3.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326189427-20800-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-11T19:36:44Z","receivedAt":"2012-01-11T19:36:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> This gives us an opportunity to abort the command during remote HEAD\n> check without wasting much bandwidth.\n>\n> Cloning with remote-helper remains before the check because the remote\n> helper updates mapped_refs, which is necessary for remote ref checks.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  I'm not familiar with remote-helper to see if there's any better way\n>  to do this..\n> ...\n> +\trefs = transport_get_remote_refs(transport);\n> +\tmapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;\n> +\n> +\t/*\n> +\t * mapped_refs may be updated if transport-helper is used so\n> +\t * we need fetch it early because remote_head code below\n> +\t * relies on it.\n> +\t *\n> +\t * for normal clones, transport_get_remote_refs() should\n> +\t * return reliable ref set, we can delay cloning until after\n> +\t * remote HEAD check.\n> +\t */\n> +\tif (!is_local && remote->foreign_vcs && refs)\n> +\t\ttransport_fetch_refs(transport, mapped_refs);\n> +\n\nI like the goal of this change, but wouldn't remote->url indicate it is a\nremote that needs a helper invocation by having double-colon in it,\nwithout having an explicit value in foreign_vcs field?\n\nWould it be cleaner if transport_get() learned to mark the transport as\nneeding special treatment (i.e. we won't know the final ref mapping until\nwe fetch the data from the other side) and check is made on that mark left\nin the transport, instead of foreign_vcs in remote?\n\n> diff --git a/transport.c b/transport.c\n> index a99b7c9..aae9889 100644\n> --- a/transport.c\n> +++ b/transport.c\n> @@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)\n>  \n>  \t\twhile (is_urlschemechar(p == url, *p))\n>  \t\t\tp++;\n> -\t\tif (!prefixcmp(p, \"::\"))\n> +\t\tif (!prefixcmp(p, \"::\")) {\n>  \t\t\thelper = xstrndup(url, p - url);\n> +\t\t\tremote->foreign_vcs = helper;\n> +\t\t}\n>  \t}\n>  \n>  \tif (helper) {\n\nAhhh, Ok. You are reusing the existing \"foreign_vcs\" field exactly for\nthat purpose. Earlier the field was strictly for configured .vcs value,\nbut now you use it also for the helper embedded within the URL, which\nsounds like the right change to me.\n\n> @@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n>  \t\tchar *handler = xmalloc(len + 1);\n>  \t\thandler[len] = 0;\n>  \t\tstrncpy(handler, url, len);\n> +\t\tremote->foreign_vcs = helper;\n>  \t\ttransport_helper_init(ret, handler);\n>  \t}\n\nThis I am not sure. What value does \"helper\" variable have at this point\nin the flow? Wouldn't it be a NULL? Or did you mean \"handler\"?\n"},{"id":"182375","messageId":"7v4nw26mbe.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326189427-20800-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-11T20:01:09Z","receivedAt":"2012-01-11T20:01:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> It does not make sense to look outside refs/heads for HEAD's target\n> (src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\n> code only allows symref in form refs/heads/...\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/clone.c |    4 ++--\n>  1 files changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 05224d7..960242f 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,\n>  static void update_head(const struct ref *our, const struct ref *remote,\n>  \t\t\tconst char *msg)\n>  {\n> -\tif (our) {\n> +\tif (our && !prefixcmp(our->name, \"refs/heads/\")) {\n>  \t\t/* Local default branch link */\n>  \t\tcreate_symref(\"HEAD\", our->name, NULL);\n>  \t\tif (!option_bare) {\n\nI think this makes sense. In the following three casees:\n\n - When cloning without --branch, if we could not find a branch that match\n   HEAD at the remote;\n\n - When cloning with --branch, if we did not see such a branch at the\n   remote; or\n\n - When cloning from an empty repository\n\nwe come here with \"our\" set to NULL. Additionally, if the remote HEAD\npoints outside refs/heads/ and the transport could detect that case\n(e.g. a helper that reads from ls-remote output), we can see our->name\noutside refs/heads/. Is there any other case where our is not NULL and\nour->name does not start with refs/heads/?\n\nThe \"else if (remote)\" clause (not shown, outside the context) that\nfollows still has comments that says it is a case for \"Source had detached\nHEAD pointing somewhere\", and needs to be adjusted for this change, no? It\nis \"(1) we know the HEAD points at a non-branch, (2) HEAD may point at a\nbranch but we do not know which one, or (3) we asked for a specific branch\nbut it did not exist there\" (cloning from an empty will have NULL in\nremote and the comment would not apply to that case).\n\n> @@ -760,7 +760,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tif (option_branch) {\n>  \t\t\tstruct strbuf head = STRBUF_INIT;\n> -\t\t\tstrbuf_addstr(&head, src_ref_prefix);\n> +\t\t\tstrbuf_addstr(&head, \"refs/heads/\");\n>  \t\t\tstrbuf_addstr(&head, option_branch);\n>  \t\t\tour_head_points_at =\n>  \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n"},{"id":"182393","messageId":"7vboq96bdj.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326189427-20800-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 09/10] clone: allow --branch to take a tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-11T23:57:28Z","receivedAt":"2012-01-11T23:57:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> @@ -766,6 +771,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n>  \t\t\tstrbuf_release(&head);\n>  \n> +\t\t\tif (!our_head_points_at) {\n> +\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n> +\t\t\t\tstrbuf_addstr(&head, option_branch);\n> +\t\t\t\tour_head_points_at =\n> +\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n> +\t\t\t\tstrbuf_release(&head);\n> +\t\t\t}\n> +\n\nOk. I think this makes sense.\n"},{"id":"182397","messageId":"CACsJy8AJxRDkz9-pm5aW70zj8AyBt1WqfSu7w45avDOVj26DpQ@mail.gmail.com","threadId":"29298","inReplyTo":"7v8vle6ng3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-12T01:27:33Z","receivedAt":"2012-01-12T01:27:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/12 Junio C Hamano <gitster@pobox.com>:\n>> @@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n>>               char *handler = xmalloc(len + 1);\n>>               handler[len] = 0;\n>>               strncpy(handler, url, len);\n>> +             remote->foreign_vcs = helper;\n>>               transport_helper_init(ret, handler);\n>>       }\n>\n> This I am not sure. What value does \"helper\" variable have at this point\n> in the flow? Wouldn't it be a NULL? Or did you mean \"handler\"?\n\nAh yes \"handler\", my bad.\n-- \nDuy\n"},{"id":"182407","messageId":"CACsJy8DRCqhMqp9n1qrXakRHTBk1SWeCWu5jp0bngXuMQPY88w@mail.gmail.com","threadId":"29298","inReplyTo":"7v4nw26mbe.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-12T03:00:17Z","receivedAt":"2012-01-12T03:00:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/12 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n>\n>> It does not make sense to look outside refs/heads for HEAD's target\n>> (src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\n>> code only allows symref in form refs/heads/...\n>>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>>  builtin/clone.c |    4 ++--\n>>  1 files changed, 2 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/builtin/clone.c b/builtin/clone.c\n>> index 05224d7..960242f 100644\n>> --- a/builtin/clone.c\n>> +++ b/builtin/clone.c\n>> @@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,\n>>  static void update_head(const struct ref *our, const struct ref *remote,\n>>                       const char *msg)\n>>  {\n>> -     if (our) {\n>> +     if (our && !prefixcmp(our->name, \"refs/heads/\")) {\n>>               /* Local default branch link */\n>>               create_symref(\"HEAD\", our->name, NULL);\n>>               if (!option_bare) {\n>\n> I think this makes sense. In the following three casees:\n>\n>  - When cloning without --branch, if we could not find a branch that match\n>   HEAD at the remote;\n>\n>  - When cloning with --branch, if we did not see such a branch at the\n>   remote; or\n>\n>  - When cloning from an empty repository\n>\n> we come here with \"our\" set to NULL. Additionally, if the remote HEAD\n> points outside refs/heads/ and the transport could detect that case\n> (e.g. a helper that reads from ls-remote output), we can see our->name\n> outside refs/heads/. Is there any other case where our is not NULL and\n> our->name does not start with refs/heads/?\n\nNo, this patch pretty much guarantees that our->name must be inside\nrefs/heads, unless remote's HEAD points outside, which makes it a\nbroken remote and git-upload-pack should refuse to serve in the first\nplace. Until --branch=tag comes into the picture.\n\n> The \"else if (remote)\" clause (not shown, outside the context) that\n> follows still has comments that says it is a case for \"Source had detached\n> HEAD pointing somewhere\", and needs to be adjusted for this change, no? It\n> is \"(1) we know the HEAD points at a non-branch, (2) HEAD may point at a\n> branch but we do not know which one, or (3) we asked for a specific branch\n> but it did not exist there\" (cloning from an empty will have NULL in\n> remote and the comment would not apply to that case).\n\nYes.\n-- \nDuy\n"},{"id":"182481","messageId":"1326439322-15648-1-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 00/10] nd/clone-detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:52Z","receivedAt":"2012-01-13T07:21:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Some comment updates after discussion and squash in the fixup patch.\nThe code is exactly the same as nd/clone-detached in pu.\n\nNguyễn Thái Ngọc Duy (10):\n  t5601: add missing && cascade\n  clone: write detached HEAD in bare repositories\n  clone: factor out checkout code\n  clone: factor out HEAD update code\n  clone: factor out remote ref writing\n  clone: delay cloning until after remote HEAD checking\n  clone: --branch=<branch> always means refs/heads/<branch>\n  clone: refuse to clone if --branch points to bogus ref\n  clone: allow --branch to take a tag\n  clone: print advice on checking out detached HEAD\n\n Documentation/git-clone.txt |    5 +-\n advice.c                    |   14 +++\n advice.h                    |    1 +\n builtin/checkout.c          |   16 +---\n builtin/clone.c             |  256 +++++++++++++++++++++++++------------------\n t/t5601-clone.sh            |   40 ++++++-\n t/t5706-clone-branch.sh     |    8 +-\n transport.c                 |    5 +-\n 8 files changed, 211 insertions(+), 134 deletions(-)\n\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182482","messageId":"1326439322-15648-2-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 01/10] t5601: add missing && cascade","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:53Z","receivedAt":"2012-01-13T07:21:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5601-clone.sh |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 87ee016..49821eb 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -9,9 +9,9 @@ test_expect_success setup '\n \trm -fr .git &&\n \ttest_create_repo src &&\n \t(\n-\t\tcd src\n-\t\t>file\n-\t\tgit add file\n+\t\tcd src &&\n+\t\t>file &&\n+\t\tgit add file &&\n \t\tgit commit -m initial\n \t)\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182483","messageId":"1326439322-15648-3-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 02/10] clone: write detached HEAD in bare repositories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:54Z","receivedAt":"2012-01-13T07:21:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If we don't write, HEAD is still at refs/heads/master as initialized\nby init-db, which may or may not match remote's HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c  |    9 +++------\n t/t5601-clone.sh |   25 ++++++++++++++++++++++++-\n 2 files changed, 27 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 86db954..8dde1ea 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t}\n \t} else if (remote_head) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tif (!option_bare) {\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   remote_head->old_sha1,\n-\t\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\t\tour_head_points_at = remote_head;\n-\t\t}\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t\tour_head_points_at = remote_head;\n \t} else {\n \t\t/* Nothing to checkout out */\n \t\tif (!option_no_checkout)\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 49821eb..e0b8db6 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -12,7 +12,10 @@ test_expect_success setup '\n \t\tcd src &&\n \t\t>file &&\n \t\tgit add file &&\n-\t\tgit commit -m initial\n+\t\tgit commit -m initial &&\n+\t\techo 1 >file &&\n+\t\tgit add file &&\n+\t\tgit commit -m updated\n \t)\n \n '\n@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '\n \n '\n \n+test_expect_success 'clone --mirror with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --mirror src mirror.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=mirror.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n+test_expect_success 'clone --bare with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --bare src bare.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=bare.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n test_expect_success 'clone --bare names the local repository <name>.git' '\n \n \tgit clone --bare src &&\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182484","messageId":"1326439322-15648-4-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 03/10] clone: factor out checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:55Z","receivedAt":"2012-01-13T07:21:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Read HEAD from disk instead of relying on local variable\nour_head_points_at, so that if earlier code fails to make HEAD\nproperly, it'll be detected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 58 insertions(+), 43 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 8dde1ea..100056d 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static int checkout(void)\n+{\n+\tunsigned char sha1[20];\n+\tchar *head;\n+\tstruct lock_file *lock_file;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree *tree;\n+\tstruct tree_desc t;\n+\tint err = 0, fd;\n+\n+\tif (option_no_checkout)\n+\t\treturn 0;\n+\n+\thead = resolve_refdup(\"HEAD\", sha1, 1, NULL);\n+\tif (!head) {\n+\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n+\t\t\t  \"unable to checkout.\\n\"));\n+\t\treturn 0;\n+\t}\n+\tif (strcmp(head, \"HEAD\")) {\n+\t\tif (prefixcmp(head, \"refs/heads/\"))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t}\n+\tfree(head);\n+\n+\t/* We need to be in the new work tree for the checkout */\n+\tsetup_work_tree();\n+\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&opts, 0, sizeof opts);\n+\topts.update = 1;\n+\topts.merge = 1;\n+\topts.fn = oneway_merge;\n+\topts.verbose_update = (option_verbosity > 0);\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tunpack_trees(1, &t, &opts);\n+\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock_file))\n+\t\tdie(_(\"unable to write new index file\"));\n+\n+\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n+\t\t\tsha1_to_hex(sha1), \"1\", NULL);\n+\n+\tif (!err && option_recursive)\n+\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n+\n+\treturn err;\n+}\n+\n static int write_one_config(const char *key, const char *value, void *data)\n {\n \treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n \t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\tour_head_points_at = remote_head;\n-\t} else {\n-\t\t/* Nothing to checkout out */\n-\t\tif (!option_no_checkout)\n-\t\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n-\t\t\t\t\"unable to checkout.\\n\"));\n-\t\toption_no_checkout = 1;\n \t}\n \n \tif (transport) {\n@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_disconnect(transport);\n \t}\n \n-\tif (!option_no_checkout) {\n-\t\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n-\t\tstruct unpack_trees_options opts;\n-\t\tstruct tree *tree;\n-\t\tstruct tree_desc t;\n-\t\tint fd;\n-\n-\t\t/* We need to be in the new work tree for the checkout */\n-\t\tsetup_work_tree();\n-\n-\t\tfd = hold_locked_index(lock_file, 1);\n-\n-\t\tmemset(&opts, 0, sizeof opts);\n-\t\topts.update = 1;\n-\t\topts.merge = 1;\n-\t\topts.fn = oneway_merge;\n-\t\topts.verbose_update = (option_verbosity > 0);\n-\t\topts.src_index = &the_index;\n-\t\topts.dst_index = &the_index;\n-\n-\t\ttree = parse_tree_indirect(our_head_points_at->old_sha1);\n-\t\tparse_tree(tree);\n-\t\tinit_tree_desc(&t, tree->buffer, tree->size);\n-\t\tunpack_trees(1, &t, &opts);\n-\n-\t\tif (write_cache(fd, active_cache, active_nr) ||\n-\t\t    commit_locked_index(lock_file))\n-\t\t\tdie(_(\"unable to write new index file\"));\n-\n-\t\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n-\t\t\t\tsha1_to_hex(our_head_points_at->old_sha1), \"1\",\n-\t\t\t\tNULL);\n-\n-\t\tif (!err && option_recursive)\n-\t\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n-\t}\n+\terr = checkout();\n \n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_top);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182485","messageId":"1326439322-15648-5-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 04/10] clone: factor out HEAD update code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:56Z","receivedAt":"2012-01-13T07:21:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"While at it, update the comment at \"if (remote_head)\"\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   41 ++++++++++++++++++++++++-----------------\n 1 files changed, 24 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 100056d..baed5ae 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,29 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static void update_head(const struct ref *our, const struct ref *remote,\n+\t\t\tconst char *msg)\n+{\n+\tif (our) {\n+\t\t/* Local default branch link */\n+\t\tcreate_symref(\"HEAD\", our->name, NULL);\n+\t\tif (!option_bare) {\n+\t\t\tconst char *head = skip_prefix(our->name, \"refs/heads/\");\n+\t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n+\t\t\tinstall_branch_config(0, head, option_origin, our->name);\n+\t\t}\n+\t} else if (remote) {\n+\t\t/*\n+\t\t * We know remote HEAD points to a non-branch, or\n+\t\t * HEAD points to a branch but we don't know which one, or\n+\t\t * we asked for a specific branch but it did not exist.\n+\t\t * Detach HEAD in all these cases.\n+\t\t */\n+\t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t}\n+}\n+\n static int checkout(void)\n {\n \tunsigned char sha1[20];\n@@ -763,23 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t      reflog_msg.buf);\n \t}\n \n-\tif (our_head_points_at) {\n-\t\t/* Local default branch link */\n-\t\tcreate_symref(\"HEAD\", our_head_points_at->name, NULL);\n-\t\tif (!option_bare) {\n-\t\t\tconst char *head = skip_prefix(our_head_points_at->name,\n-\t\t\t\t\t\t       \"refs/heads/\");\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   our_head_points_at->old_sha1,\n-\t\t\t\t   NULL, 0, DIE_ON_ERR);\n-\t\t\tinstall_branch_config(0, head, option_origin,\n-\t\t\t\t\t      our_head_points_at->name);\n-\t\t}\n-\t} else if (remote_head) {\n-\t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n-\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t}\n+\tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n \tif (transport) {\n \t\ttransport_unlock_pack(transport);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182486","messageId":"1326439322-15648-6-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 05/10] clone: factor out remote ref writing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:57Z","receivedAt":"2012-01-13T07:21:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   36 ++++++++++++++++++++++++------------\n 1 files changed, 24 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex baed5ae..d1d6dca 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -448,6 +448,28 @@ static void write_remote_refs(const struct ref *local_refs)\n \tclear_extra_refs();\n }\n \n+static void update_remote_refs(const struct ref *refs,\n+\t\t\t       const struct ref *mapped_refs,\n+\t\t\t       const struct ref *remote_head_points_at,\n+\t\t\t       const char *branch_top,\n+\t\t\t       const char *msg)\n+{\n+\tif (refs) {\n+\t\tclear_extra_refs();\n+\t\twrite_remote_refs(mapped_refs);\n+\t}\n+\n+\tif (remote_head_points_at && !option_bare) {\n+\t\tstruct strbuf head_ref = STRBUF_INIT;\n+\t\tstrbuf_addstr(&head_ref, branch_top);\n+\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n+\t\tcreate_symref(head_ref.buf,\n+\t\t\t      remote_head_points_at->peer_ref->name,\n+\t\t\t      msg);\n+\t}\n+\n+}\n+\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n@@ -740,10 +762,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (refs) {\n-\t\tclear_extra_refs();\n-\n-\t\twrite_remote_refs(mapped_refs);\n-\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n \t\t\tguess_remote_head(remote_head, mapped_refs, 0);\n@@ -777,14 +795,8 @@ 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 (remote_head_points_at && !option_bare) {\n-\t\tstruct strbuf head_ref = STRBUF_INIT;\n-\t\tstrbuf_addstr(&head_ref, branch_top.buf);\n-\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n-\t\tcreate_symref(head_ref.buf,\n-\t\t\t      remote_head_points_at->peer_ref->name,\n-\t\t\t      reflog_msg.buf);\n-\t}\n+\tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n+\t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182487","messageId":"1326439322-15648-7-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:58Z","receivedAt":"2012-01-13T07:21:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This gives us an opportunity to abort the command during remote HEAD\ncheck without wasting much bandwidth.\n\nCloning with remote-helper remains before the check because the remote\nhelper updates mapped_refs, which is necessary for remote ref checks.\nforeign_vcs field is used to indicate the transport is handled by\nremote helper.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------\n transport.c     |    5 ++++-\n 2 files changed, 31 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex d1d6dca..a9d06a4 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -361,13 +361,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,\n \tclosedir(dir);\n }\n \n-static const struct ref *clone_local(const char *src_repo,\n-\t\t\t\t     const char *dest_repo)\n+static void clone_local(const char *src_repo, const char *dest_repo)\n {\n-\tconst struct ref *ret;\n-\tstruct remote *remote;\n-\tstruct transport *transport;\n-\n \tif (option_shared) {\n \t\tstruct strbuf alt = STRBUF_INIT;\n \t\tstrbuf_addf(&alt, \"%s/objects\", src_repo);\n@@ -383,13 +378,8 @@ static const struct ref *clone_local(const char *src_repo,\n \t\tstrbuf_release(&dest);\n \t}\n \n-\tremote = remote_get(src_repo);\n-\ttransport = transport_get(remote, src_repo);\n-\tret = transport_get_remote_refs(transport);\n-\ttransport_disconnect(transport);\n \tif (0 <= option_verbosity)\n \t\tprintf(_(\"done.\\n\"));\n-\treturn ret;\n }\n \n static const char *junk_work_tree;\n@@ -581,6 +571,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tchar *src_ref_prefix = \"refs/heads/\";\n+\tstruct remote *remote;\n \tint err = 0;\n \n \tstruct refspec *refspec;\n@@ -732,13 +723,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_reset(&value);\n \n-\tif (is_local) {\n-\t\trefs = clone_local(path, git_dir);\n-\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t} else {\n-\t\tstruct remote *remote = remote_get(option_origin);\n-\t\ttransport = transport_get(remote, remote->url[0]);\n+\tremote = remote_get(option_origin);\n+\ttransport = transport_get(remote, remote->url[0]);\n \n+\tif (!is_local) {\n \t\tif (!transport->get_refs_list || !transport->fetch)\n \t\t\tdie(_(\"Don't know how to clone %s\"), transport->url);\n \n@@ -753,14 +741,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (option_upload_pack)\n \t\t\ttransport_set_option(transport, TRANS_OPT_UPLOADPACK,\n \t\t\t\t\t     option_upload_pack);\n-\n-\t\trefs = transport_get_remote_refs(transport);\n-\t\tif (refs) {\n-\t\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t\t\ttransport_fetch_refs(transport, mapped_refs);\n-\t\t}\n \t}\n \n+\trefs = transport_get_remote_refs(transport);\n+\tmapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;\n+\n+\t/*\n+\t * mapped_refs may be updated if transport-helper is used so\n+\t * we need fetch it early because remote_head code below\n+\t * relies on it.\n+\t *\n+\t * for normal clones, transport_get_remote_refs() should\n+\t * return reliable ref set, we can delay cloning until after\n+\t * remote HEAD check.\n+\t */\n+\tif (!is_local && remote->foreign_vcs && refs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tif (refs) {\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n@@ -795,15 +792,18 @@ 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 (is_local)\n+\t\tclone_local(path, git_dir);\n+\telse if (refs && !remote->foreign_vcs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n \t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-\tif (transport) {\n-\t\ttransport_unlock_pack(transport);\n-\t\ttransport_disconnect(transport);\n-\t}\n+\ttransport_unlock_pack(transport);\n+\ttransport_disconnect(transport);\n \n \terr = checkout();\n \ndiff --git a/transport.c b/transport.c\nindex a99b7c9..4366639 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \t\twhile (is_urlschemechar(p == url, *p))\n \t\t\tp++;\n-\t\tif (!prefixcmp(p, \"::\"))\n+\t\tif (!prefixcmp(p, \"::\")) {\n \t\t\thelper = xstrndup(url, p - url);\n+\t\t\tremote->foreign_vcs = helper;\n+\t\t}\n \t}\n \n \tif (helper) {\n@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\tchar *handler = xmalloc(len + 1);\n \t\thandler[len] = 0;\n \t\tstrncpy(handler, url, len);\n+\t\tremote->foreign_vcs = handler;\n \t\ttransport_helper_init(ret, handler);\n \t}\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182488","messageId":"1326439322-15648-8-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 07/10] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:21:59Z","receivedAt":"2012-01-13T07:21:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It does not make sense to look outside refs/heads for HEAD's target\n(src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\ncode only allows symref in form refs/heads/...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex a9d06a4..89ba6f0 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n-\tif (our) {\n+\tif (our && !prefixcmp(our->name, \"refs/heads/\")) {\n \t\t/* Local default branch link */\n \t\tcreate_symref(\"HEAD\", our->name, NULL);\n \t\tif (!option_bare) {\n@@ -765,7 +765,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tif (option_branch) {\n \t\t\tstruct strbuf head = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&head, src_ref_prefix);\n+\t\t\tstrbuf_addstr(&head, \"refs/heads/\");\n \t\t\tstrbuf_addstr(&head, option_branch);\n \t\t\tour_head_points_at =\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182489","messageId":"1326439322-15648-9-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 08/10] clone: refuse to clone if --branch points to bogus ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:22:00Z","receivedAt":"2012-01-13T07:22:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's possible that users make a typo in the branch name. Stop and let\nusers recheck. Falling back to remote's HEAD is not documented any\nway.\n\nExcept when using remote helper, the pack has not been transferred at\nthis stage yet so we don't waste much bandwidth.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c         |   13 +++++--------\n t/t5706-clone-branch.sh |    8 ++------\n 2 files changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 89ba6f0..6a2886a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -474,8 +474,7 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t} else if (remote) {\n \t\t/*\n \t\t * We know remote HEAD points to a non-branch, or\n-\t\t * HEAD points to a branch but we don't know which one, or\n-\t\t * we asked for a specific branch but it did not exist.\n+\t\t * HEAD points to a branch but we don't know which one.\n \t\t * Detach HEAD in all these cases.\n \t\t */\n \t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n@@ -771,12 +770,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n \t\t\tstrbuf_release(&head);\n \n-\t\t\tif (!our_head_points_at) {\n-\t\t\t\twarning(_(\"Remote branch %s not found in \"\n-\t\t\t\t\t\"upstream %s, using HEAD instead\"),\n-\t\t\t\t\toption_branch, option_origin);\n-\t\t\t\tour_head_points_at = remote_head_points_at;\n-\t\t\t}\n+\t\t\tif (!our_head_points_at)\n+\t\t\t\tdie(_(\"Remote branch %s not found in \"\n+\t\t\t\t      \"upstream %s, using HEAD instead\"),\n+\t\t\t\t    option_branch, option_origin);\n \t\t}\n \t\telse\n \t\t\tour_head_points_at = remote_head_points_at;\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nindex f3f9a76..56be67e 100755\n--- a/t/t5706-clone-branch.sh\n+++ b/t/t5706-clone-branch.sh\n@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '\n \t)\n '\n \n-test_expect_success 'clone -b with bogus branch chooses HEAD' '\n-\tgit clone -b bogus parent clone-bogus &&\n-\t(cd clone-bogus &&\n-\t check_HEAD master &&\n-\t check_file one\n-\t)\n+test_expect_success 'clone -b with bogus branch' '\n+\ttest_must_fail git clone -b bogus parent clone-bogus\n '\n \n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182490","messageId":"1326439322-15648-10-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 09/10] clone: allow --branch to take a tag","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:22:01Z","receivedAt":"2012-01-13T07:22:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Because a tag ref cannot be put to HEAD, HEAD will become detached.\nThis is consistent with \"git checkout <tag>\".\n\nThis is mostly useful in shallow clone, where it allows you to clone a\ntag in addtion to branches.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |    5 +++--\n builtin/clone.c             |   13 +++++++++++++\n t/t5601-clone.sh            |    9 +++++++++\n 3 files changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 4b8b26b..db2b29c 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -146,8 +146,9 @@ 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. In a non-bare repository, this is the branch that will\n-\tbe checked out.\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 \n --upload-pack <upload-pack>::\n -u <upload-pack>::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 6a2886a..a3c8f78 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -471,6 +471,11 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n \t\t\tinstall_branch_config(0, head, option_origin, our->name);\n \t\t}\n+\t} else if (our) {\n+\t\tstruct commit *c = lookup_commit_reference(our->old_sha1);\n+\t\t/* --branch specifies a non-branch (i.e. tags), detach HEAD */\n+\t\tupdate_ref(msg, \"HEAD\", c->object.sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n \t} else if (remote) {\n \t\t/*\n \t\t * We know remote HEAD points to a non-branch, or\n@@ -770,6 +775,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n \t\t\tstrbuf_release(&head);\n \n+\t\t\tif (!our_head_points_at) {\n+\t\t\t\tstrbuf_addstr(&head, \"refs/tags/\");\n+\t\t\t\tstrbuf_addstr(&head, option_branch);\n+\t\t\t\tour_head_points_at =\n+\t\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n+\t\t\t\tstrbuf_release(&head);\n+\t\t\t}\n+\n \t\t\tif (!our_head_points_at)\n \t\t\t\tdie(_(\"Remote branch %s not found in \"\n \t\t\t\t      \"upstream %s, using HEAD instead\"),\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e0b8db6..67869b4 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '\n \tgrep /src/\\\\.git/objects target-10/objects/info/alternates\n '\n \n+test_expect_success 'clone checking out a tag' '\n+\tgit clone --branch=some-tag src dst.tag &&\n+\tGIT_DIR=src/.git git rev-parse some-tag >expected &&\n+\ttest_cmp expected dst.tag/.git/HEAD &&\n+\tGIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" >fetch.expected &&\n+\ttest_cmp fetch.expected fetch.actual\n+'\n+\n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182491","messageId":"1326439322-15648-11-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326189427-20800-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v4 10/10] clone: print advice on checking out detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-13T07:22:02Z","receivedAt":"2012-01-13T07:22:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c           |   14 ++++++++++++++\n advice.h           |    1 +\n builtin/checkout.c |   16 +---------------\n builtin/clone.c    |    5 ++++-\n 4 files changed, 20 insertions(+), 16 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex e02e632..3e1a145 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)\n \terror_resolve_conflict(me);\n \tdie(\"Exiting because of an unresolved conflict.\");\n }\n+\n+void detach_advice(const char *new_name)\n+{\n+\tconst char fmt[] =\n+\t\"Note: checking out '%s'.\\n\\n\"\n+\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n+\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n+\t\"  git checkout -b new_branch_name\\n\\n\";\n+\n+\tfprintf(stderr, fmt, new_name);\n+}\ndiff --git a/advice.h b/advice.h\nindex e5d0af7..7bda45b 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);\n void advise(const char *advice, ...);\n int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n+void detach_advice(const char *new_name);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f1984d9..5bf96ba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n-static void detach_advice(const char *old_path, const char *new_name)\n-{\n-\tconst char fmt[] =\n-\t\"Note: checking out '%s'.\\n\\n\"\n-\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n-\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n-\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b new_branch_name\\n\\n\";\n-\n-\tfprintf(stderr, fmt, new_name);\n-}\n-\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n \t\t\tif (old->path && advice_detached_head)\n-\t\t\t\tdetach_advice(old->path, new->name);\n+\t\t\t\tdetach_advice(new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n \t} else if (new->path) {\t/* Switch branches. */\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex a3c8f78..bfdafaa 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -506,7 +506,10 @@ static int checkout(void)\n \t\t\t  \"unable to checkout.\\n\"));\n \t\treturn 0;\n \t}\n-\tif (strcmp(head, \"HEAD\")) {\n+\tif (!strcmp(head, \"HEAD\")) {\n+\t\tif (advice_detached_head)\n+\t\t\tdetach_advice(sha1_to_hex(sha1));\n+\t} else {\n \t\tif (prefixcmp(head, \"refs/heads/\"))\n \t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n \t}\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182520","messageId":"7vvcofxtv8.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v4 00/10] nd/clone-detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-13T19:52:43Z","receivedAt":"2012-01-13T19:52:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks, replaced (and updated comment strings read much better).\n\nThere were some conlicts I had to resolve while merging this to 'pu'.\nI would appreciate it if you can eyeball it to make sure I didn't make\nsilly mistakes there.\n\nThanks.\n"},{"id":"182530","messageId":"CACsJy8DggQdM1aoeL+u=3Wz+5f7hi4eG=6MHXPCJZ6pOmhQJ_w@mail.gmail.com","threadId":"29298","inReplyTo":"7vvcofxtv8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 00/10] nd/clone-detached","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-14T04:48:25Z","receivedAt":"2012-01-14T04:48:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/14 Junio C Hamano <gitster@pobox.com>:\n> Thanks, replaced (and updated comment strings read much better).\n>\n> There were some conlicts I had to resolve while merging this to 'pu'.\n> I would appreciate it if you can eyeball it to make sure I didn't make\n> silly mistakes there.\n\nRight, the conflict with nd/clone-single-branch. I kept thinking there\nwould not be conflict because clone-single-branch's big change was in\nwanted_peer_refs() and missed write_followtags() call. The merge looks\ngood.\n-- \nDuy\n"},{"id":"182534","messageId":"7vhazyyduw.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"CACsJy8DggQdM1aoeL+u=3Wz+5f7hi4eG=6MHXPCJZ6pOmhQJ_w@mail.gmail.com","subject":"Re: [PATCH v4 00/10] nd/clone-detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-14T06:53:11Z","receivedAt":"2012-01-14T06:53:11Z","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> 2012/1/14 Junio C Hamano <gitster@pobox.com>:\n>> Thanks, replaced (and updated comment strings read much better).\n>>\n>> There were some conlicts I had to resolve while merging this to 'pu'.\n>> I would appreciate it if you can eyeball it to make sure I didn't make\n>> silly mistakes there.\n>\n> Right, the conflict with nd/clone-single-branch. I kept thinking there\n> would not be conflict because clone-single-branch's big change was in\n> wanted_peer_refs() and missed write_followtags() call. The merge looks\n> good.\n\nHmm, 'pu' seems to fail its selftest with this merge present, though.\n"},{"id":"182538","messageId":"20120114074036.GA27256@do","threadId":"29298","inReplyTo":"7vhazyyduw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v4 00/10] nd/clone-detached","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-14T07:40:36Z","receivedAt":"2012-01-14T07:40:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Jan 13, 2012 at 10:53:11PM -0800, Junio C Hamano wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n> \n> > 2012/1/14 Junio C Hamano <gitster@pobox.com>:\n> >> Thanks, replaced (and updated comment strings read much better).\n> >>\n> >> There were some conlicts I had to resolve while merging this to 'pu'.\n> >> I would appreciate it if you can eyeball it to make sure I didn't make\n> >> silly mistakes there.\n> >\n> > Right, the conflict with nd/clone-single-branch. I kept thinking there\n> > would not be conflict because clone-single-branch's big change was in\n> > wanted_peer_refs() and missed write_followtags() call. The merge looks\n> > good.\n> \n> Hmm, 'pu' seems to fail its selftest with this merge present, though.\n\nThe commit \"refuse to clone if --branch points to bogus ref\" from this\nseries changes clone's behavior that t5500.31, which is added in\nnd/clone-single-branch, relies on. This makes \"make test\" pass for me\non pu:\n\n-- 8< --\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 7e85c71..c4e675f 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -283,10 +283,7 @@ test_expect_success 'clone shallow object count' '\n '\n \n test_expect_success 'clone shallow with nonexistent --branch' '\n-\tgit clone --depth 1 --branch Z \"file://$(pwd)/.\" shallow4 &&\n-\tGIT_DIR=shallow4/.git git rev-parse HEAD >actual &&\n-\tgit rev-parse HEAD >expected &&\n-\ttest_cmp expected actual\n+\ttest_must_fail git clone --depth 1 --branch Z \"file://$(pwd)/.\" shallow4\n '\n \n test_expect_success 'clone shallow with detached HEAD' '\n-- 8< --\n\nI'd rather remove the test, but removing something in a merge does not\nsound wise.\n\nAnother cleaner approach is to combine the two clone series into\none. If you want to go this way, pick one as base, I'll rebase the\nother on top and resend.\n-- \nDuy\n"},{"id":"182571","messageId":"7vk44twv68.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"20120114074036.GA27256@do","subject":"Re: [PATCH v4 00/10] nd/clone-detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-15T02:34:23Z","receivedAt":"2012-01-15T02:34:23Z","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>> Hmm, 'pu' seems to fail its selftest with this merge present, though.\n>\n> The commit \"refuse to clone if --branch points to bogus ref\" from this\n> series changes clone's behavior that t5500.31, which is added in\n> nd/clone-single-branch, relies on.\n\nAhh, of course.\n\n> Another cleaner approach is to combine the two clone series into\n> one.\n\nI think \"--single-branch\" is much more relevant compared to cloning from a\nrepository with some funkiness like detached HEAD which is more about a\ntheoretical fun exercise, so let's rebuild this on top of the other one.\n\nThanks.\n"},{"id":"182599","messageId":"1326707176-8045-1-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 00/10] nd/clone-detached rebase onto nd/clone-single-branch","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:06Z","receivedAt":"2012-01-16T09:46:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"There are a few more changes than just a simple rebase so that\n\"git clone --depth=1 --branch=tag\" also works.\n\nNguyễn Thái Ngọc Duy (10):\n  t5601: add missing && cascade\n  clone: write detached HEAD in bare repositories\n  clone: factor out checkout code\n  clone: factor out HEAD update code\n  clone: factor out remote ref writing\n  clone: delay cloning until after remote HEAD checking\n  clone: --branch=<branch> always means refs/heads/<branch>\n  clone: refuse to clone if --branch points to bogus ref\n  clone: allow --branch to take a tag\n  clone: print advice on checking out detached HEAD\n\n Documentation/git-clone.txt |    5 +-\n advice.c                    |   14 ++\n advice.h                    |    1 +\n builtin/checkout.c          |   16 +--\n builtin/clone.c             |  297 +++++++++++++++++++++++++------------------\n t/t5500-fetch-pack.sh       |   22 ++-\n t/t5601-clone.sh            |   40 +++++-\n t/t5706-clone-branch.sh     |    8 +-\n transport.c                 |    5 +-\n 9 files changed, 249 insertions(+), 159 deletions(-)\n\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182600","messageId":"1326707176-8045-2-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 01/10] t5601: add missing && cascade","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:07Z","receivedAt":"2012-01-16T09:46:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5601-clone.sh |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 87ee016..49821eb 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -9,9 +9,9 @@ test_expect_success setup '\n \trm -fr .git &&\n \ttest_create_repo src &&\n \t(\n-\t\tcd src\n-\t\t>file\n-\t\tgit add file\n+\t\tcd src &&\n+\t\t>file &&\n+\t\tgit add file &&\n \t\tgit commit -m initial\n \t)\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182601","messageId":"1326707176-8045-3-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 02/10] clone: write detached HEAD in bare repositories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:08Z","receivedAt":"2012-01-16T09:46:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If we don't write, HEAD is still at refs/heads/master as initialized\nby init-db, which may or may not match remote's HEAD.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c  |    9 +++------\n t/t5601-clone.sh |   25 ++++++++++++++++++++++++-\n 2 files changed, 27 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 9dcc5fe..91862f7 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -764,12 +764,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t}\n \t} else if (remote_head) {\n \t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tif (!option_bare) {\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   remote_head->old_sha1,\n-\t\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\t\tour_head_points_at = remote_head;\n-\t\t}\n+\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t\tour_head_points_at = remote_head;\n \t} else {\n \t\t/* Nothing to checkout out */\n \t\tif (!option_no_checkout)\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 49821eb..e0b8db6 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -12,7 +12,10 @@ test_expect_success setup '\n \t\tcd src &&\n \t\t>file &&\n \t\tgit add file &&\n-\t\tgit commit -m initial\n+\t\tgit commit -m initial &&\n+\t\techo 1 >file &&\n+\t\tgit add file &&\n+\t\tgit commit -m updated\n \t)\n \n '\n@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '\n \n '\n \n+test_expect_success 'clone --mirror with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --mirror src mirror.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=mirror.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n+test_expect_success 'clone --bare with detached HEAD' '\n+\n+\t( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&\n+\tgit clone --bare src bare.detached &&\n+\t( cd src && git checkout - ) &&\n+\tGIT_DIR=bare.detached git rev-parse HEAD >actual &&\n+\ttest_cmp expected actual\n+\n+'\n+\n test_expect_success 'clone --bare names the local repository <name>.git' '\n \n \tgit clone --bare src &&\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182602","messageId":"1326707176-8045-4-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 03/10] clone: factor out checkout code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:09Z","receivedAt":"2012-01-16T09:46:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Read HEAD from disk instead of relying on local variable\nour_head_points_at, so that if earlier code fails to make HEAD\nproperly, it'll be detected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 58 insertions(+), 43 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 91862f7..98e3542 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -486,6 +486,63 @@ static void write_followtags(const struct ref *refs, const char *msg)\n \t}\n }\n \n+static int checkout(void)\n+{\n+\tunsigned char sha1[20];\n+\tchar *head;\n+\tstruct lock_file *lock_file;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree *tree;\n+\tstruct tree_desc t;\n+\tint err = 0, fd;\n+\n+\tif (option_no_checkout)\n+\t\treturn 0;\n+\n+\thead = resolve_refdup(\"HEAD\", sha1, 1, NULL);\n+\tif (!head) {\n+\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n+\t\t\t  \"unable to checkout.\\n\"));\n+\t\treturn 0;\n+\t}\n+\tif (strcmp(head, \"HEAD\")) {\n+\t\tif (prefixcmp(head, \"refs/heads/\"))\n+\t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n+\t}\n+\tfree(head);\n+\n+\t/* We need to be in the new work tree for the checkout */\n+\tsetup_work_tree();\n+\n+\tlock_file = xcalloc(1, sizeof(struct lock_file));\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&opts, 0, sizeof opts);\n+\topts.update = 1;\n+\topts.merge = 1;\n+\topts.fn = oneway_merge;\n+\topts.verbose_update = (option_verbosity > 0);\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tunpack_trees(1, &t, &opts);\n+\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock_file))\n+\t\tdie(_(\"unable to write new index file\"));\n+\n+\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n+\t\t\tsha1_to_hex(sha1), \"1\", NULL);\n+\n+\tif (!err && option_recursive)\n+\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n+\n+\treturn err;\n+}\n+\n static int write_one_config(const char *key, const char *value, void *data)\n {\n \treturn git_config_set_multivar(key, value ? value : \"true\", \"^$\", 0);\n@@ -766,13 +823,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t/* Source had detached HEAD pointing somewhere. */\n \t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n \t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t\tour_head_points_at = remote_head;\n-\t} else {\n-\t\t/* Nothing to checkout out */\n-\t\tif (!option_no_checkout)\n-\t\t\twarning(_(\"remote HEAD refers to nonexistent ref, \"\n-\t\t\t\t\"unable to checkout.\\n\"));\n-\t\toption_no_checkout = 1;\n \t}\n \n \tif (transport) {\n@@ -780,42 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_disconnect(transport);\n \t}\n \n-\tif (!option_no_checkout) {\n-\t\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n-\t\tstruct unpack_trees_options opts;\n-\t\tstruct tree *tree;\n-\t\tstruct tree_desc t;\n-\t\tint fd;\n-\n-\t\t/* We need to be in the new work tree for the checkout */\n-\t\tsetup_work_tree();\n-\n-\t\tfd = hold_locked_index(lock_file, 1);\n-\n-\t\tmemset(&opts, 0, sizeof opts);\n-\t\topts.update = 1;\n-\t\topts.merge = 1;\n-\t\topts.fn = oneway_merge;\n-\t\topts.verbose_update = (option_verbosity > 0);\n-\t\topts.src_index = &the_index;\n-\t\topts.dst_index = &the_index;\n-\n-\t\ttree = parse_tree_indirect(our_head_points_at->old_sha1);\n-\t\tparse_tree(tree);\n-\t\tinit_tree_desc(&t, tree->buffer, tree->size);\n-\t\tunpack_trees(1, &t, &opts);\n-\n-\t\tif (write_cache(fd, active_cache, active_nr) ||\n-\t\t    commit_locked_index(lock_file))\n-\t\t\tdie(_(\"unable to write new index file\"));\n-\n-\t\terr |= run_hook(NULL, \"post-checkout\", sha1_to_hex(null_sha1),\n-\t\t\t\tsha1_to_hex(our_head_points_at->old_sha1), \"1\",\n-\t\t\t\tNULL);\n-\n-\t\tif (!err && option_recursive)\n-\t\t\terr = run_command_v_opt(argv_submodule, RUN_GIT_CMD);\n-\t}\n+\terr = checkout();\n \n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_top);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182603","messageId":"1326707176-8045-5-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 04/10] clone: factor out HEAD update code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:10Z","receivedAt":"2012-01-16T09:46:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"While at it, update the comment at \"if (remote_head)\"\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   41 ++++++++++++++++++++++++-----------------\n 1 files changed, 24 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 98e3542..3b68014 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)\n \t}\n }\n \n+static void update_head(const struct ref *our, const struct ref *remote,\n+\t\t\tconst char *msg)\n+{\n+\tif (our) {\n+\t\t/* Local default branch link */\n+\t\tcreate_symref(\"HEAD\", our->name, NULL);\n+\t\tif (!option_bare) {\n+\t\t\tconst char *head = skip_prefix(our->name, \"refs/heads/\");\n+\t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n+\t\t\tinstall_branch_config(0, head, option_origin, our->name);\n+\t\t}\n+\t} else if (remote) {\n+\t\t/*\n+\t\t * We know remote HEAD points to a non-branch, or\n+\t\t * HEAD points to a branch but we don't know which one, or\n+\t\t * we asked for a specific branch but it did not exist.\n+\t\t * Detach HEAD in all these cases.\n+\t\t */\n+\t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t}\n+}\n+\n static int checkout(void)\n {\n \tunsigned char sha1[20];\n@@ -807,23 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t      reflog_msg.buf);\n \t}\n \n-\tif (our_head_points_at) {\n-\t\t/* Local default branch link */\n-\t\tcreate_symref(\"HEAD\", our_head_points_at->name, NULL);\n-\t\tif (!option_bare) {\n-\t\t\tconst char *head = skip_prefix(our_head_points_at->name,\n-\t\t\t\t\t\t       \"refs/heads/\");\n-\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n-\t\t\t\t   our_head_points_at->old_sha1,\n-\t\t\t\t   NULL, 0, DIE_ON_ERR);\n-\t\t\tinstall_branch_config(0, head, option_origin,\n-\t\t\t\t\t      our_head_points_at->name);\n-\t\t}\n-\t} else if (remote_head) {\n-\t\t/* Source had detached HEAD pointing somewhere. */\n-\t\tupdate_ref(reflog_msg.buf, \"HEAD\", remote_head->old_sha1,\n-\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n-\t}\n+\tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n \tif (transport) {\n \t\ttransport_unlock_pack(transport);\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182604","messageId":"1326707176-8045-6-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 05/10] clone: factor out remote ref writing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:11Z","receivedAt":"2012-01-16T09:46:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   39 +++++++++++++++++++++++++--------------\n 1 files changed, 25 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 3b68014..2733fa4 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)\n \t}\n }\n \n+static void update_remote_refs(const struct ref *refs,\n+\t\t\t       const struct ref *mapped_refs,\n+\t\t\t       const struct ref *remote_head_points_at,\n+\t\t\t       const char *branch_top,\n+\t\t\t       const char *msg)\n+{\n+\tif (refs) {\n+\t\tclear_extra_refs();\n+\t\twrite_remote_refs(mapped_refs);\n+\t\tif (option_single_branch)\n+\t\t\twrite_followtags(refs, msg);\n+\t}\n+\n+\tif (remote_head_points_at && !option_bare) {\n+\t\tstruct strbuf head_ref = STRBUF_INIT;\n+\t\tstrbuf_addstr(&head_ref, branch_top);\n+\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n+\t\tcreate_symref(head_ref.buf,\n+\t\t\t      remote_head_points_at->peer_ref->name,\n+\t\t\t      msg);\n+\t}\n+}\n+\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n@@ -782,12 +805,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (refs) {\n-\t\tclear_extra_refs();\n-\n-\t\twrite_remote_refs(mapped_refs);\n-\t\tif (option_single_branch)\n-\t\t\twrite_followtags(refs, reflog_msg.buf);\n-\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n \t\t\tguess_remote_head(remote_head, mapped_refs, 0);\n@@ -821,14 +838,8 @@ 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 (remote_head_points_at && !option_bare) {\n-\t\tstruct strbuf head_ref = STRBUF_INIT;\n-\t\tstrbuf_addstr(&head_ref, branch_top.buf);\n-\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n-\t\tcreate_symref(head_ref.buf,\n-\t\t\t      remote_head_points_at->peer_ref->name,\n-\t\t\t      reflog_msg.buf);\n-\t}\n+\tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n+\t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182605","messageId":"1326707176-8045-7-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:12Z","receivedAt":"2012-01-16T09:46:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This gives us an opportunity to abort the command during remote HEAD\ncheck without wasting much bandwidth.\n\nCloning with remote-helper remains before the check because the remote\nhelper updates mapped_refs, which is necessary for remote ref checks.\nforeign_vcs field is used to indicate the transport is handled by\nremote helper.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------\n transport.c     |    5 ++++-\n 2 files changed, 31 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 2733fa4..a1fbb3b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -364,13 +364,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,\n \tclosedir(dir);\n }\n \n-static const struct ref *clone_local(const char *src_repo,\n-\t\t\t\t     const char *dest_repo)\n+static void clone_local(const char *src_repo, const char *dest_repo)\n {\n-\tconst struct ref *ret;\n-\tstruct remote *remote;\n-\tstruct transport *transport;\n-\n \tif (option_shared) {\n \t\tstruct strbuf alt = STRBUF_INIT;\n \t\tstrbuf_addf(&alt, \"%s/objects\", src_repo);\n@@ -386,13 +381,8 @@ static const struct ref *clone_local(const char *src_repo,\n \t\tstrbuf_release(&dest);\n \t}\n \n-\tremote = remote_get(src_repo);\n-\ttransport = transport_get(remote, src_repo);\n-\tret = transport_get_remote_refs(transport);\n-\ttransport_disconnect(transport);\n \tif (0 <= option_verbosity)\n \t\tprintf(_(\"done.\\n\"));\n-\treturn ret;\n }\n \n static const char *junk_work_tree;\n@@ -619,6 +609,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n+\tstruct remote *remote;\n \tint err = 0;\n \n \tstruct refspec *refspec;\n@@ -773,13 +764,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_reset(&value);\n \n-\tif (is_local) {\n-\t\trefs = clone_local(path, git_dir);\n-\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t} else {\n-\t\tstruct remote *remote = remote_get(option_origin);\n-\t\ttransport = transport_get(remote, remote->url[0]);\n+\tremote = remote_get(option_origin);\n+\ttransport = transport_get(remote, remote->url[0]);\n \n+\tif (!is_local) {\n \t\tif (!transport->get_refs_list || !transport->fetch)\n \t\t\tdie(_(\"Don't know how to clone %s\"), transport->url);\n \n@@ -796,14 +784,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (option_upload_pack)\n \t\t\ttransport_set_option(transport, TRANS_OPT_UPLOADPACK,\n \t\t\t\t\t     option_upload_pack);\n-\n-\t\trefs = transport_get_remote_refs(transport);\n-\t\tif (refs) {\n-\t\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n-\t\t\ttransport_fetch_refs(transport, mapped_refs);\n-\t\t}\n \t}\n \n+\trefs = transport_get_remote_refs(transport);\n+\tmapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;\n+\n+\t/*\n+\t * mapped_refs may be updated if transport-helper is used so\n+\t * we need fetch it early because remote_head code below\n+\t * relies on it.\n+\t *\n+\t * for normal clones, transport_get_remote_refs() should\n+\t * return reliable ref set, we can delay cloning until after\n+\t * remote HEAD check.\n+\t */\n+\tif (!is_local && remote->foreign_vcs && refs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tif (refs) {\n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n \t\tremote_head_points_at =\n@@ -838,15 +835,18 @@ 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 (is_local)\n+\t\tclone_local(path, git_dir);\n+\telse if (refs && !remote->foreign_vcs)\n+\t\ttransport_fetch_refs(transport, mapped_refs);\n+\n \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n \t\t\t   branch_top.buf, reflog_msg.buf);\n \n \tupdate_head(our_head_points_at, remote_head, reflog_msg.buf);\n \n-\tif (transport) {\n-\t\ttransport_unlock_pack(transport);\n-\t\ttransport_disconnect(transport);\n-\t}\n+\ttransport_unlock_pack(transport);\n+\ttransport_disconnect(transport);\n \n \terr = checkout();\n \ndiff --git a/transport.c b/transport.c\nindex a99b7c9..4366639 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \n \t\twhile (is_urlschemechar(p == url, *p))\n \t\t\tp++;\n-\t\tif (!prefixcmp(p, \"::\"))\n+\t\tif (!prefixcmp(p, \"::\")) {\n \t\t\thelper = xstrndup(url, p - url);\n+\t\t\tremote->foreign_vcs = helper;\n+\t\t}\n \t}\n \n \tif (helper) {\n@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\tchar *handler = xmalloc(len + 1);\n \t\thandler[len] = 0;\n \t\tstrncpy(handler, url, len);\n+\t\tremote->foreign_vcs = handler;\n \t\ttransport_helper_init(ret, handler);\n \t}\n \n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182606","messageId":"1326707176-8045-8-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch>","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:13Z","receivedAt":"2012-01-16T09:46:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It does not make sense to look outside refs/heads for HEAD's target\n(src_ref_prefix can be set to \"refs/\" if --mirror is used) because ref\ncode only allows symref in form refs/heads/...\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c |   30 ++++++++++++++++--------------\n 1 files changed, 16 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex a1fbb3b..253a794 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -48,7 +48,6 @@ static int option_verbosity;\n static int option_progress;\n static struct string_list option_config;\n static struct string_list option_reference;\n-static const char *src_ref_prefix = \"refs/heads/\";\n \n static int opt_parse_reference(const struct option *opt, const char *arg, int unset)\n {\n@@ -413,6 +412,17 @@ static void remove_junk_on_signal(int signo)\n \traise(signo);\n }\n \n+static struct ref *find_remote_branch(const struct ref *refs, const char *branch)\n+{\n+\tstruct ref *ref;\n+\tstruct strbuf head = STRBUF_INIT;\n+\tstrbuf_addstr(&head, \"refs/heads/\");\n+\tstrbuf_addstr(&head, branch);\n+\tref = find_ref_by_name(refs, head.buf);\n+\tstrbuf_release(&head);\n+\treturn ref;\n+}\n+\n static struct ref *wanted_peer_refs(const struct ref *refs,\n \t\tstruct refspec *refspec)\n {\n@@ -425,13 +435,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs,\n \n \t\tif (!option_branch)\n \t\t\tremote_head = guess_remote_head(head, refs, 0);\n-\t\telse {\n-\t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&sb, src_ref_prefix);\n-\t\t\tstrbuf_addstr(&sb, option_branch);\n-\t\t\tremote_head = find_ref_by_name(refs, sb.buf);\n-\t\t\tstrbuf_release(&sb);\n-\t\t}\n+\t\telse\n+\t\t\tremote_head = find_remote_branch(refs, option_branch);\n \n \t\tif (!remote_head && option_branch)\n \t\t\twarning(_(\"Could not find remote branch %s to clone.\"),\n@@ -502,7 +507,7 @@ static void update_remote_refs(const struct ref *refs,\n static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tconst char *msg)\n {\n-\tif (our) {\n+\tif (our && !prefixcmp(our->name, \"refs/heads/\")) {\n \t\t/* Local default branch link */\n \t\tcreate_symref(\"HEAD\", our->name, NULL);\n \t\tif (!option_bare) {\n@@ -609,6 +614,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n+\tconst char *src_ref_prefix = \"refs/heads/\";\n \tstruct remote *remote;\n \tint err = 0;\n \n@@ -807,12 +813,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tguess_remote_head(remote_head, mapped_refs, 0);\n \n \t\tif (option_branch) {\n-\t\t\tstruct strbuf head = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&head, src_ref_prefix);\n-\t\t\tstrbuf_addstr(&head, option_branch);\n \t\t\tour_head_points_at =\n-\t\t\t\tfind_ref_by_name(mapped_refs, head.buf);\n-\t\t\tstrbuf_release(&head);\n+\t\t\t\tfind_remote_branch(mapped_refs, option_branch);\n \n \t\t\tif (!our_head_points_at) {\n \t\t\t\twarning(_(\"Remote branch %s not found in \"\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182607","messageId":"1326707176-8045-9-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:14Z","receivedAt":"2012-01-16T09:46:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"It's possible that users make a typo in the branch name. Stop and let\nusers recheck. Falling back to remote's HEAD is not documented any\nway.\n\nExcept when using remote helper, the pack has not been transferred at\nthis stage yet so we don't waste much bandwidth.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/clone.c         |   12 ++++--------\n t/t5500-fetch-pack.sh   |    7 -------\n t/t5706-clone-branch.sh |    8 ++------\n 3 files changed, 6 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 253a794..3cfedb3 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -518,8 +518,7 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t} else if (remote) {\n \t\t/*\n \t\t * We know remote HEAD points to a non-branch, or\n-\t\t * HEAD points to a branch but we don't know which one, or\n-\t\t * we asked for a specific branch but it did not exist.\n+\t\t * HEAD points to a branch but we don't know which one.\n \t\t * Detach HEAD in all these cases.\n \t\t */\n \t\tupdate_ref(msg, \"HEAD\", remote->old_sha1,\n@@ -816,12 +815,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tour_head_points_at =\n \t\t\t\tfind_remote_branch(mapped_refs, option_branch);\n \n-\t\t\tif (!our_head_points_at) {\n-\t\t\t\twarning(_(\"Remote branch %s not found in \"\n-\t\t\t\t\t\"upstream %s, using HEAD instead\"),\n-\t\t\t\t\toption_branch, option_origin);\n-\t\t\t\tour_head_points_at = remote_head_points_at;\n-\t\t\t}\n+\t\t\tif (!our_head_points_at)\n+\t\t\t\tdie(_(\"Remote branch %s not found in upstream %s\"),\n+\t\t\t\t    option_branch, option_origin);\n \t\t}\n \t\telse\n \t\t\tour_head_points_at = remote_head_points_at;\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 7e85c71..5237066 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -282,13 +282,6 @@ test_expect_success 'clone shallow object count' '\n \ttest_cmp count3.expected count3.actual\n '\n \n-test_expect_success 'clone shallow with nonexistent --branch' '\n-\tgit clone --depth 1 --branch Z \"file://$(pwd)/.\" shallow4 &&\n-\tGIT_DIR=shallow4/.git git rev-parse HEAD >actual &&\n-\tgit rev-parse HEAD >expected &&\n-\ttest_cmp expected actual\n-'\n-\n test_expect_success 'clone shallow with detached HEAD' '\n \tgit checkout HEAD^ &&\n \tgit clone --depth 1 \"file://$(pwd)/.\" shallow5 &&\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nindex f3f9a76..56be67e 100755\n--- a/t/t5706-clone-branch.sh\n+++ b/t/t5706-clone-branch.sh\n@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '\n \t)\n '\n \n-test_expect_success 'clone -b with bogus branch chooses HEAD' '\n-\tgit clone -b bogus parent clone-bogus &&\n-\t(cd clone-bogus &&\n-\t check_HEAD master &&\n-\t check_file one\n-\t)\n+test_expect_success 'clone -b with bogus branch' '\n+\ttest_must_fail git clone -b bogus parent clone-bogus\n '\n \n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182608","messageId":"1326707176-8045-10-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 09/10] clone: allow --branch to take a tag","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:15Z","receivedAt":"2012-01-16T09:46:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Because a tag ref cannot be put to HEAD, HEAD will become detached.\nThis is consistent with \"git checkout <tag>\".\n\nThis is mostly useful in shallow clone, where it allows you to clone a\ntag in addtion to branches.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |    5 +++--\n builtin/clone.c             |   20 +++++++++++++++++++-\n t/t5500-fetch-pack.sh       |   15 +++++++++++++++\n t/t5601-clone.sh            |    9 +++++++++\n 4 files changed, 46 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 0931a3e..6e22522 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -147,8 +147,9 @@ 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. In a non-bare repository, this is the branch that will\n-\tbe checked out.\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 \n --upload-pack <upload-pack>::\n -u <upload-pack>::\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 3cfedb3..651b4cc 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -420,6 +420,15 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch\n \tstrbuf_addstr(&head, branch);\n \tref = find_ref_by_name(refs, head.buf);\n \tstrbuf_release(&head);\n+\n+\tif (ref)\n+\t\treturn ref;\n+\n+\tstrbuf_addstr(&head, \"refs/tags/\");\n+\tstrbuf_addstr(&head, branch);\n+\tref = find_ref_by_name(refs, head.buf);\n+\tstrbuf_release(&head);\n+\n \treturn ref;\n }\n \n@@ -441,8 +450,12 @@ static struct ref *wanted_peer_refs(const struct ref *refs,\n \t\tif (!remote_head && option_branch)\n \t\t\twarning(_(\"Could not find remote branch %s to clone.\"),\n \t\t\t\toption_branch);\n-\t\telse\n+\t\telse {\n \t\t\tget_fetch_map(remote_head, refspec, &tail, 0);\n+\n+\t\t\t/* if --branch=tag, pull the requested tag explicitly */\n+\t\t\tget_fetch_map(remote_head, tag_refspec, &tail, 0);\n+\t\t}\n \t} else\n \t\tget_fetch_map(refs, refspec, &tail, 0);\n \n@@ -515,6 +528,11 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t\t\tupdate_ref(msg, \"HEAD\", our->old_sha1, NULL, 0, DIE_ON_ERR);\n \t\t\tinstall_branch_config(0, head, option_origin, our->name);\n \t\t}\n+\t} else if (our) {\n+\t\tstruct commit *c = lookup_commit_reference(our->old_sha1);\n+\t\t/* --branch specifies a non-branch (i.e. tags), detach HEAD */\n+\t\tupdate_ref(msg, \"HEAD\", c->object.sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n \t} else if (remote) {\n \t\t/*\n \t\t * We know remote HEAD points to a non-branch, or\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 5237066..ce51692 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -311,4 +311,19 @@ EOF\n \ttest_cmp count6.expected count6.actual\n '\n \n+test_expect_success 'shallow cloning single tag' '\n+\tgit clone --depth 1 --branch=TAGB1 \"file://$(pwd)/.\" shallow7 &&\n+\tcat >taglist.expected <<\\EOF &&\n+TAGB1\n+TAGB2\n+EOF\n+\tGIT_DIR=shallow7/.git git tag -l >taglist.actual &&\n+\ttest_cmp taglist.expected taglist.actual &&\n+\n+\techo \"in-pack: 7\" > count7.expected &&\n+\tGIT_DIR=shallow7/.git git count-objects -v |\n+\t\tgrep \"^in-pack\" > count7.actual &&\n+\ttest_cmp count7.expected count7.actual\n+'\n+\n test_done\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e0b8db6..67869b4 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '\n \tgrep /src/\\\\.git/objects target-10/objects/info/alternates\n '\n \n+test_expect_success 'clone checking out a tag' '\n+\tgit clone --branch=some-tag src dst.tag &&\n+\tGIT_DIR=src/.git git rev-parse some-tag >expected &&\n+\ttest_cmp expected dst.tag/.git/HEAD &&\n+\tGIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&\n+\techo \"+refs/heads/*:refs/remotes/origin/*\" >fetch.expected &&\n+\ttest_cmp fetch.expected fetch.actual\n+'\n+\n test_done\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182609","messageId":"1326707176-8045-11-git-send-email-pclouds@gmail.com","threadId":"29298","inReplyTo":"1326439322-15648-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v5 10/10] clone: print advice on checking out detached HEAD","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-16T09:46:16Z","receivedAt":"2012-01-16T09:46:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n advice.c           |   14 ++++++++++++++\n advice.h           |    1 +\n builtin/checkout.c |   16 +---------------\n builtin/clone.c    |    5 ++++-\n 4 files changed, 20 insertions(+), 16 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex e02e632..3e1a145 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)\n \terror_resolve_conflict(me);\n \tdie(\"Exiting because of an unresolved conflict.\");\n }\n+\n+void detach_advice(const char *new_name)\n+{\n+\tconst char fmt[] =\n+\t\"Note: checking out '%s'.\\n\\n\"\n+\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n+\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n+\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n+\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n+\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n+\t\"  git checkout -b new_branch_name\\n\\n\";\n+\n+\tfprintf(stderr, fmt, new_name);\n+}\ndiff --git a/advice.h b/advice.h\nindex e5d0af7..7bda45b 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);\n void advise(const char *advice, ...);\n int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n+void detach_advice(const char *new_name);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f1984d9..5bf96ba 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n-static void detach_advice(const char *old_path, const char *new_name)\n-{\n-\tconst char fmt[] =\n-\t\"Note: checking out '%s'.\\n\\n\"\n-\t\"You are in 'detached HEAD' state. You can look around, make experimental\\n\"\n-\t\"changes and commit them, and you can discard any commits you make in this\\n\"\n-\t\"state without impacting any branches by performing another checkout.\\n\\n\"\n-\t\"If you want to create a new branch to retain commits you create, you may\\n\"\n-\t\"do so (now or later) by using -b with the checkout command again. Example:\\n\\n\"\n-\t\"  git checkout -b new_branch_name\\n\\n\";\n-\n-\tfprintf(stderr, fmt, new_name);\n-}\n-\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n \t\t\tif (old->path && advice_detached_head)\n-\t\t\t\tdetach_advice(old->path, new->name);\n+\t\t\t\tdetach_advice(new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n \t} else if (new->path) {\t/* Switch branches. */\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 651b4cc..72eebca 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -563,7 +563,10 @@ static int checkout(void)\n \t\t\t  \"unable to checkout.\\n\"));\n \t\treturn 0;\n \t}\n-\tif (strcmp(head, \"HEAD\")) {\n+\tif (!strcmp(head, \"HEAD\")) {\n+\t\tif (advice_detached_head)\n+\t\t\tdetach_advice(sha1_to_hex(sha1));\n+\t} else {\n \t\tif (prefixcmp(head, \"refs/heads/\"))\n \t\t\tdie(_(\"HEAD not found below refs/heads!\"));\n \t}\n-- \n1.7.3.1.256.g2539c.dirty\n"},{"id":"182995","messageId":"7v62g26jm3.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"1326189427-20800-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-24T00:15:48Z","receivedAt":"2012-01-24T00:15:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I wanted to merge this series to 'next' and further merge it early in the\nnext cycle to 'master', but with this my pushes (!!) seem to fail.\n\nIt breaks pushing to multiple URLs like this:\n\n    $ cat .git/config\n    [remote \"origin\"]\n            url = https://code.google.com/p/git-htmldocs/\n            url = github.com:gitster/git-htmldocs.git\n            push = refs/heads/master:refs/heads/master\n    $ git push\n\nThe second url that is supposed to use the git-over-ssh transport\nmistakenly use https:// and fails with:\n\n    error: Couldn't resolve host 'github.com:gitster' while accessing\n    github.com:gitster/git-htmldocs.git/info/refs\n    fatal: HTTP request failed\n"},{"id":"182997","messageId":"7vzkde546x.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"7v62g26jm3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-24T00:34:14Z","receivedAt":"2012-01-24T00:34:14Z","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> It breaks pushing to multiple URLs like this:\n>\n>     $ cat .git/config\n>     [remote \"origin\"]\n>             url = https://code.google.com/p/git-htmldocs/\n>             url = github.com:gitster/git-htmldocs.git\n>             push = refs/heads/master:refs/heads/master\n>     $ git push\n>\n> The second url that is supposed to use the git-over-ssh transport\n> mistakenly use https:// and fails with:\n>\n>     error: Couldn't resolve host 'github.com:gitster' while accessing\n>     github.com:gitster/git-htmldocs.git/info/refs\n>     fatal: HTTP request failed\n\nAnd here is an obvious band-aid to work it around.\n"},{"id":"182998","messageId":"7vvco253xx.fsf@alter.siamese.dyndns.org","threadId":"29298","inReplyTo":"7vzkde546x.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-01-24T00:39:38Z","receivedAt":"2012-01-24T00:39:38Z","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> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> It breaks pushing to multiple URLs like this:\n>>\n>>     $ cat .git/config\n>>     [remote \"origin\"]\n>>             url = https://code.google.com/p/git-htmldocs/\n>>             url = github.com:gitster/git-htmldocs.git\n>>             push = refs/heads/master:refs/heads/master\n>>     $ git push\n>>\n>> The second url that is supposed to use the git-over-ssh transport\n>> mistakenly use https:// and fails with:\n>>\n>>     error: Couldn't resolve host 'github.com:gitster' while accessing\n>>     github.com:gitster/git-htmldocs.git/info/refs\n>>     fatal: HTTP request failed\n>\n> And here is an obvious band-aid to work it around.\n\n-- >8 --\nSubject: [PATCH] push: do not let configured foreign-vcs permanently clobbered\n\nRecently, 6f48d39 (clone: delay cloning until after remote HEAD checking,\n2012-01-16) tried to record if a remote helper needs to be called after\nparsing the remote when transport_get() is called, by overwriting the\nfield meant to store the configured remote helper name in the remote\nstructure.\n\nThis is OK when a remote represents a single remote repository, but fails\nmiserably when pushing to locations with multiple URLs, like this:\n\n    $ cat .git/config\n    [remote \"origin\"]\n        url = https://code.google.com/p/git-htmldocs/\n        url = github.com:gitster/git-htmldocs.git\n        push = refs/heads/master:refs/heads/master\n    $ git push\n\nThe second url that is supposed to use the git-over-ssh transport\nmistakenly use https:// and fails with:\n\n    error: Couldn't resolve host 'github.com:gitster' while accessing\n    github.com:gitster/git-htmldocs.git/info/refs\n    fatal: HTTP request failed\n\nThe right solution would probably be to dedicate a separate field to store\nthe detected external helper to be used, which is valid only during a\nsingle use of transport until it is disconnected, instead of overwriting\nforeign_vcs field, but in the meantime, this band-aid should suffice.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin/push.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 35cce53..5fb98a0 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -204,11 +204,13 @@ static int do_push(const char *repo, int flags)\n \t\turl_nr = remote->url_nr;\n \t}\n \tif (url_nr) {\n+\t\tconst char *configured_foreign_vcs = remote->foreign_vcs;\n \t\tfor (i = 0; i < url_nr; i++) {\n \t\t\tstruct transport *transport =\n \t\t\t\ttransport_get(remote, url[i]);\n \t\t\tif (push_with_options(transport, flags))\n \t\t\t\terrs++;\n+\t\t\tremote->foreign_vcs = configured_foreign_vcs;\n \t\t}\n \t} else {\n \t\tstruct transport *transport =\n-- \n1.7.9.rc2.100.gfd863d\n"},{"id":"183010","messageId":"CACsJy8C_Lzgk-W3D9ArTh4xQBgFcy41UQzTHfghYKrGg7WxHMg@mail.gmail.com","threadId":"29298","inReplyTo":"7vvco253xx.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-01-24T07:01:59Z","receivedAt":"2012-01-24T07:01:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2012/1/24 Junio C Hamano <gitster@pobox.com>:\n> Recently, 6f48d39 (clone: delay cloning until after remote HEAD checking,\n> 2012-01-16) tried to record if a remote helper needs to be called after\n> parsing the remote when transport_get() is called, by overwriting the\n> field meant to store the configured remote helper name in the remote\n> structure.\n>\n> This is OK when a remote represents a single remote repository, but fails\n> miserably when pushing to locations with multiple URLs, like this:\n>\n>    $ cat .git/config\n>    [remote \"origin\"]\n>        url = https://code.google.com/p/git-htmldocs/\n>        url = github.com:gitster/git-htmldocs.git\n>        push = refs/heads/master:refs/heads/master\n\nMy bad. remote.*.vcs (or remote->foreign_vcs) is designed to override\nremote helper detection based on url. Once you have pushed over https,\nthe following urls will be over https too. Luckily no other places\nperform an operation over multiple urls like push so we're safe with\nyour bandage.\n\nWe should have another way to detect whether remote helper being used\n(comparing some function pointers like transport->connect or\ntransport->disconnect may help). But I need to think a bit about\ndelaying cloning for http(s) too even though remote helper is used.\n-- \nDuy\n"}]}