{"thread":{"id":"20733","subject":"[PATCH] Add option -b/--branch to clone for select a new HEAD","startedAt":"2009-08-25T19:25:41Z","lastAt":"2009-08-28T12:10:02Z","messageCount":15,"participants":["Kirill A. Korinskiy","Jeff King","Björn Steinbrink","Junio C Hamano","Tor Arne Vestbø","Martin Langhoff","Julian Phillips"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"121750","messageId":"1251228341-29434-1-git-send-email-catap@catap.ru","threadId":"20733","inReplyTo":"87praj90n8.wl%catap@catap.ru","subject":"[PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Kirill A. Korinskiy","fromEmail":"catap@catap.ru","sentAt":"2009-08-25T19:25:41Z","receivedAt":"2009-08-25T19:25:41Z","isPatch":true,"sender":{"key":"catap@catap.ru","avatar":"https://gravatar.com/avatar/ea0ab2c29579606bd684eccdf786c666f4425bedc4e599b698a175f12737b1c5?d=mp&s=160"},"body":"Sometimes (especially on production systems) we need to use only one\nremote branch for building software. It really annoying to clone\norigin and then swith branch by hand everytime. So this patch provide\nfunctionality to clone remote branch with one command without using\ncheckout after clone.\n\nSigned-off-by: Kirill A. Korinskiy <catap@catap.ru>\n---\n Documentation/git-clone.txt |    4 ++++\n builtin-clone.c             |   23 ++++++++++++++++++++---\n t/t5706-clone-branch.sh     |   31 +++++++++++++++++++++++++++++++\n 3 files changed, 55 insertions(+), 3 deletions(-)\n create mode 100755 t/t5706-clone-branch.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 2c63a0f..50446d2 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -127,6 +127,10 @@ objects from the source repository into a pack in the cloned repository.\n \tInstead of using the remote name 'origin' to keep track\n \tof the upstream repository, use <name>.\n \n+--branch <name>::\n+-b <name>::\n+\tInstead of using the remote HEAD as master, use <name> branch.\n+\n --upload-pack <upload-pack>::\n -u <upload-pack>::\n \tWhen given, and the repository to clone from is accessed\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 32dea74..9cea056 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n static char *option_template, *option_reference, *option_depth;\n static char *option_origin = NULL;\n+static char *option_branch = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n \n@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {\n \t\t   \"reference repository\"),\n \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n+\tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n+\t\t   \"use <branch> from 'origin' as HEAD\"),\n \tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n \t\t   \"path to git-upload-pack on the remote\"),\n \tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n@@ -347,8 +350,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir;\n \tint dest_exists;\n-\tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n-\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n+\tconst struct ref *refs, *head_points_at, *remote_head = NULL, *mapped_refs;\n+\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT, branch_head = STRBUF_INIT;\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tchar *src_ref_prefix = \"refs/heads/\";\n@@ -518,7 +521,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n \n-\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n+\t\tif (option_branch) {\n+\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n+\t\t}\n+\n+\t\tif (!remote_head) {\n+\t\t\tif (option_branch)\n+\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n+\t\t\t\t\t\", using HEAD instead\",\n+\t\t\t\t\toption_branch, option_origin);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n+\t\t}\n+\n \t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n \t}\n \telse {\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nnew file mode 100755\nindex 0000000..8d83ac8\n--- /dev/null\n+++ b/t/t5706-clone-branch.sh\n@@ -0,0 +1,31 @@\n+#!/bin/sh\n+\n+test_description='branch clone options'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tmkdir parent &&\n+\t(cd parent && git init &&\n+\t echo one >file && git add file &&\n+\t git commit -m one && git checkout -b two &&\n+\t echo two >f && git add f && git commit -m two &&\n+\t git checkout master)\n+\n+'\n+\n+test_expect_success 'clone' '\n+\n+\tgit clone parent clone &&\n+\t(cd clone && git rev-parse --verify refs/remotes/origin/master)\n+\n+'\n+\n+test_expect_success 'clone -b' '\n+\n+\tgit clone -b two parent clone-b &&\n+\t(cd clone-b && test $(git rev-parse --verify HEAD) = $(git rev-parse --verify refs/remotes/origin/two))\n+\n+'\n+\n+test_done\n-- \n1.6.2\n"},{"id":"121751","messageId":"1251228467-29638-1-git-send-email-catap@catap.ru","threadId":"20733","inReplyTo":"1251228341-29434-1-git-send-email-catap@catap.ru","subject":"[PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Kirill A. Korinskiy","fromEmail":"catap@catap.ru","sentAt":"2009-08-25T19:27:47Z","receivedAt":"2009-08-25T19:27:47Z","isPatch":true,"sender":{"key":"catap@catap.ru","avatar":"https://gravatar.com/avatar/ea0ab2c29579606bd684eccdf786c666f4425bedc4e599b698a175f12737b1c5?d=mp&s=160"},"body":"Sometimes (especially on production systems) we need to use only one\nremote branch for building software. It's really annoying to clone\norigin and then switch branch by hand everytime. So this patch\nprovides functionality to clone a remote branch with one command\nwithout using checkout after clone.\n\nSigned-off-by: Kirill A. Korinskiy <catap@catap.ru>\n---\n Documentation/git-clone.txt |    4 ++++\n builtin-clone.c             |   23 ++++++++++++++++++++---\n t/t5706-clone-branch.sh     |   31 +++++++++++++++++++++++++++++++\n 3 files changed, 55 insertions(+), 3 deletions(-)\n create mode 100755 t/t5706-clone-branch.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 2c63a0f..50446d2 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -127,6 +127,10 @@ objects from the source repository into a pack in the cloned repository.\n \tInstead of using the remote name 'origin' to keep track\n \tof the upstream repository, use <name>.\n \n+--branch <name>::\n+-b <name>::\n+\tInstead of using the remote HEAD as master, use <name> branch.\n+\n --upload-pack <upload-pack>::\n -u <upload-pack>::\n \tWhen given, and the repository to clone from is accessed\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 32dea74..9cea056 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n static char *option_template, *option_reference, *option_depth;\n static char *option_origin = NULL;\n+static char *option_branch = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n \n@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {\n \t\t   \"reference repository\"),\n \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n+\tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n+\t\t   \"use <branch> from 'origin' as HEAD\"),\n \tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n \t\t   \"path to git-upload-pack on the remote\"),\n \tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n@@ -347,8 +350,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir;\n \tint dest_exists;\n-\tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n-\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n+\tconst struct ref *refs, *head_points_at, *remote_head = NULL, *mapped_refs;\n+\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT, branch_head = STRBUF_INIT;\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tchar *src_ref_prefix = \"refs/heads/\";\n@@ -518,7 +521,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n \n-\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n+\t\tif (option_branch) {\n+\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n+\t\t}\n+\n+\t\tif (!remote_head) {\n+\t\t\tif (option_branch)\n+\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n+\t\t\t\t\t\", using HEAD instead\",\n+\t\t\t\t\toption_branch, option_origin);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n+\t\t}\n+\n \t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n \t}\n \telse {\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nnew file mode 100755\nindex 0000000..8d83ac8\n--- /dev/null\n+++ b/t/t5706-clone-branch.sh\n@@ -0,0 +1,31 @@\n+#!/bin/sh\n+\n+test_description='branch clone options'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tmkdir parent &&\n+\t(cd parent && git init &&\n+\t echo one >file && git add file &&\n+\t git commit -m one && git checkout -b two &&\n+\t echo two >f && git add f && git commit -m two &&\n+\t git checkout master)\n+\n+'\n+\n+test_expect_success 'clone' '\n+\n+\tgit clone parent clone &&\n+\t(cd clone && git rev-parse --verify refs/remotes/origin/master)\n+\n+'\n+\n+test_expect_success 'clone -b' '\n+\n+\tgit clone -b two parent clone-b &&\n+\t(cd clone-b && test $(git rev-parse --verify HEAD) = $(git rev-parse --verify refs/remotes/origin/two))\n+\n+'\n+\n+test_done\n-- \n1.6.2\n"},{"id":"121756","messageId":"20090825215726.GA30981@coredump.intra.peff.net","threadId":"20733","inReplyTo":"1251228467-29638-1-git-send-email-catap@catap.ru","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-08-25T21:57:26Z","receivedAt":"2009-08-25T21:57:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 25, 2009 at 11:27:47PM +0400, Kirill A. Korinskiy wrote:\n\n> +test_expect_success 'clone' '\n> +\n> +\tgit clone parent clone &&\n> +\t(cd clone && git rev-parse --verify refs/remotes/origin/master)\n> +\n> +'\n> +\n> +test_expect_success 'clone -b' '\n> +\n> +\tgit clone -b two parent clone-b &&\n> +\t(cd clone-b && test $(git rev-parse --verify HEAD) = $(git rev-parse --verify refs/remotes/origin/two))\n> +\n> +'\n\nOK, I think that second test makes sense (though please wrap the very\nlong line), but now what is the first one doing? Shouldn't it be:\n\n  (cd clone &&\n   test $(git rev-parse --verify HEAD) = \\\n        $(git rev-parse --verify refs/remotes/origin/master)\n  )\n\nalso?\n\n-Peff\n"},{"id":"121758","messageId":"20090825223637.GA20523@atjola.homenet","threadId":"20733","inReplyTo":"1251228467-29638-1-git-send-email-catap@catap.ru","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Björn Steinbrink","fromEmail":"b.steinbrink@gmx.de","sentAt":"2009-08-25T22:36:37Z","receivedAt":"2009-08-25T22:36:37Z","isPatch":true,"sender":{"key":"b.steinbrink@gmx.de","avatar":"https://avatars.githubusercontent.com/u/230962?v=4"},"body":"On 2009.08.25 23:27:47 +0400, Kirill A. Korinskiy wrote:\n> Sometimes (especially on production systems) we need to use only one\n> remote branch for building software. It's really annoying to clone\n> origin and then switch branch by hand everytime. So this patch\n> provides functionality to clone a remote branch with one command\n> without using checkout after clone.\n> \n> Signed-off-by: Kirill A. Korinskiy <catap@catap.ru>\n> ---\n>  Documentation/git-clone.txt |    4 ++++\n>  builtin-clone.c             |   23 ++++++++++++++++++++---\n>  t/t5706-clone-branch.sh     |   31 +++++++++++++++++++++++++++++++\n>  3 files changed, 55 insertions(+), 3 deletions(-)\n>  create mode 100755 t/t5706-clone-branch.sh\n> \n> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> index 2c63a0f..50446d2 100644\n> --- a/Documentation/git-clone.txt\n> +++ b/Documentation/git-clone.txt\n> @@ -127,6 +127,10 @@ objects from the source repository into a pack in the cloned repository.\n>  \tInstead of using the remote name 'origin' to keep track\n>  \tof the upstream repository, use <name>.\n>  \n> +--branch <name>::\n> +-b <name>::\n> +\tInstead of using the remote HEAD as master, use <name> branch.\n\nHm, that's no good. The branch won't be called master, nor is HEAD used\nas \"master\" anyway. If the remote repo's HEAD references refs/heads/foo,\nyou'll get refs/heads/foo locally as well, not \"master\", but see below.\n\nMaybe: Create a local branch head for <name> instead of the branch\nreferenced by the remote repo's HEAD.\n\n> @@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {\n>  \t\t   \"reference repository\"),\n>  \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n>  \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n> +\tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n> +\t\t   \"use <branch> from 'origin' as HEAD\"),\n\nUsing 'origin' there is unfortunate, as using \"--origin foo\" would make\nclone call the remote \"foo\" instead of \"origin\". And it's not really\nused \"as\" HEAD, but instead of the remote repo's HEAD to determine which\nlocal branch head to create. Though I guess this affect the\nrefs/remotes/<remote>/HEAD symref as well?\n\n> @@ -518,7 +521,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n>  \n> -\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> +\t\tif (option_branch) {\n> +\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n> +\n> +\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n> +\t\t}\n> +\n> +\t\tif (!remote_head) {\n> +\t\t\tif (option_branch)\n> +\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n> +\t\t\t\t\t\", using HEAD instead\",\n> +\t\t\t\t\toption_branch, option_origin);\n> +\n> +\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> +\t\t}\n> +\n>  \t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n\nThis would still pick refs/heads/master if refs/heads/master and\nrefs/heads/<branch> reference the same commit. That's due to the check\nin guess_remote_head() which prefers refs/heads/master over all other\nrefs. While this is acceptable for the HEAD lookup, I'd treat that as a\nbug for this new option.\n\n>  \t}\n>  \telse {\n> diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\n> new file mode 100755\n> index 0000000..8d83ac8\n> --- /dev/null\n> +++ b/t/t5706-clone-branch.sh\n> @@ -0,0 +1,31 @@\n> +#!/bin/sh\n> +\n> +test_description='branch clone options'\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\n> +\tmkdir parent &&\n> +\t(cd parent && git init &&\n> +\t echo one >file && git add file &&\n> +\t git commit -m one && git checkout -b two &&\n> +\t echo two >f && git add f && git commit -m two &&\n> +\t git checkout master)\n> +\n> +'\n> +\n> +test_expect_success 'clone' '\n> +\n> +\tgit clone parent clone &&\n> +\t(cd clone && git rev-parse --verify refs/remotes/origin/master)\n> +\n> +'\n> +\n> +test_expect_success 'clone -b' '\n> +\n> +\tgit clone -b two parent clone-b &&\n> +\t(cd clone-b && test $(git rev-parse --verify HEAD) = $(git rev-parse --verify refs/remotes/origin/two))\n\nThis should probably check not just that HEAD resolves to the same\ncommit as refs/remotes/origin/two, but that HEAD references\nrefs/heads/two as well, even if the remote's refs/heads/master\nreferences the same commit as refs/heads/two (see above).\n\nBjörn\n"},{"id":"121759","messageId":"7vvdkbms6b.fsf@alter.siamese.dyndns.org","threadId":"20733","inReplyTo":"20090825215726.GA30981@coredump.intra.peff.net","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-08-25T22:42:36Z","receivedAt":"2009-08-25T22:42:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Aug 25, 2009 at 11:27:47PM +0400, Kirill A. Korinskiy wrote:\n>\n>> +test_expect_success 'clone' '\n>> +\n>> +\tgit clone parent clone &&\n>> +\t(cd clone && git rev-parse --verify refs/remotes/origin/master)\n>> +\n>> +'\n>> +\n>> +test_expect_success 'clone -b' '\n>> +\n>> +\tgit clone -b two parent clone-b &&\n>> +\t(cd clone-b && test $(git rev-parse --verify HEAD) = $(git rev-parse --verify refs/remotes/origin/two))\n>> +\n>> +'\n>\n> OK, I think that second test makes sense (though please wrap the very\n> long line), but now what is the first one doing? Shouldn't it be:\n>\n>   (cd clone &&\n>    test $(git rev-parse --verify HEAD) = \\\n>         $(git rev-parse --verify refs/remotes/origin/master)\n>   )\n>\n> also?\n\nAre you checking that the HEAD (whichever branch it points at) points at\nthe same commit, or are you also interested in the _current branch_ to be\na particular name as well?  The suggested check only compares commits and\nHEAD can be pointing at a local branch whose name is xyzzy.\n\nWhat is the semantics of this new -b option?  When the remote repository\nhas 'next' as its default branch (i.e. HEAD points at it), and if you run\nclone with \"-b maint\" against it, I expect that the checked out commit\nwill be the 'maint' of remote repository, but what is the name of the\ncurrent branch in the resulting clone on our end?\n\n - Would we use 'master' as the name of our current branch, because that\n   is the default?\n\n - Would we use 'next' as the name of our current branch, because that is\n   what the remote side uses?\n\n - Would we use 'maint', because that is what -b gave us?\n\nI am _hoping_ it is the last one, as otherwise you would also need to make\nsure that the branch that is different from 'maint' we set as the current\nbranch must track 'maint' from the remote.\n\nOh, with -b, would we set up our 'maint' to track their 'maint'?  Is it\nsomething you may want to verify as well?\n"},{"id":"121796","messageId":"20090826121600.GA29098@atjola.homenet","threadId":"20733","inReplyTo":"87ljl694fd.wl%catap@catap.ru","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Björn Steinbrink","fromEmail":"b.steinbrink@gmx.de","sentAt":"2009-08-26T12:16:00Z","receivedAt":"2009-08-26T12:16:00Z","isPatch":true,"sender":{"key":"b.steinbrink@gmx.de","avatar":"https://avatars.githubusercontent.com/u/230962?v=4"},"body":"On 2009.08.26 15:53:58 +0400, Kirill A. Korinskiy wrote:\n> At Wed, 26 Aug 2009 00:36:37 +0200,\n> Björn Steinbrink <B.Steinbrink@gmx.de> wrote:\n> \n> \n> > > @@ -518,7 +521,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> > >  \n> > >  \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n> > >  \n> > > -\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> > > +\t\tif (option_branch) {\n> > > +\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n> > > +\n> > > +\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n> > > +\t\t}\n> > > +\n> > > +\t\tif (!remote_head) {\n> > > +\t\t\tif (option_branch)\n> > > +\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n> > > +\t\t\t\t\t\", using HEAD instead\",\n> > > +\t\t\t\t\toption_branch, option_origin);\n> > > +\n> > > +\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> > > +\t\t}\n> > > +\n> > >  \t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n> > \n> > This would still pick refs/heads/master if refs/heads/master and\n> > refs/heads/<branch> reference the same commit. That's due to the check\n> > in guess_remote_head() which prefers refs/heads/master over all other\n> > refs. While this is acceptable for the HEAD lookup, I'd treat that as a\n> > bug for this new option.\n> > \n> \n> My english is not a good and I don't understand it, sorry.\n\nguess_remote_head() compares the object ids from remote_head and all of\nthe remote's refs to guess which is the right one.\n\nLet's say that the repo has:\n\nrefs/heads/master: object1\nrefs/heads/foo: object2\nrefs/heads/bar: object1\n\nIf you do \"git clone -b foo ...\", then remote_head->old_sha1 will be\n\"object2\". guess_remote_head() compares that to all the remote heads. In\nthis case, it will find refs/heads/foo (as expected).\n\nBut when you do \"git clone -b bar\", then remote_head->old_sha1 will be\n\"object1\". And guess_remote_head() will then take refs/heads/master,\nas it prefers that one.\n\ndoener@atjola:h $ mkdir a; cd a; git init\nInitialized empty Git repository in /home/doener/h/a/.git/\ndoener@atjola:a (master) $ git commit --allow-empty -m init\n[master (root-commit) a7a0b54] init\ndoener@atjola:a (master) $ git branch bar\ndoener@atjola:a (master) $ git checkout -b foo\nSwitched to a new branch 'foo'\ndoener@atjola:a (foo) $ git commit --allow-empty -m on_foo\n[foo 375047e] on_foo\ndoener@atjola:a (foo) $ cd ..\ndoener@atjola:h $ (git clone -b foo a foo; cd foo; git branch)\nInitialized empty Git repository in /home/doener/h/foo/.git/\n* foo\ndoener@atjola:h $ (git clone -b bar a bar; cd bar; git branch)\nInitialized empty Git repository in /home/doener/h/bar/.git/\n* master\n\n\nThat said, I actually wonder why you don't simple set HEAD in the\noriginal repo so that you get whichever branch you want by default\nanyway.\n\nBjörn\n"},{"id":"121806","messageId":"1251298007-18693-1-git-send-email-catap@catap.ru","threadId":"20733","inReplyTo":"20090826121600.GA29098@atjola.homenet","subject":"[PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Kirill A. Korinskiy","fromEmail":"catap@catap.ru","sentAt":"2009-08-26T14:46:47Z","receivedAt":"2009-08-26T14:46:47Z","isPatch":true,"sender":{"key":"catap@catap.ru","avatar":"https://gravatar.com/avatar/ea0ab2c29579606bd684eccdf786c666f4425bedc4e599b698a175f12737b1c5?d=mp&s=160"},"body":"Sometimes (especially on production systems) we need to use only one\nremote branch for building software. It's really annoying to clone\norigin and then switch branch by hand everytime. So this patch\nprovides functionality to clone a remote branch with one command\nwithout using checkout after clone.\n\nSigned-off-by: Kirill A. Korinskiy <catap@catap.ru>\n---\n Documentation/git-clone.txt |    5 ++++\n builtin-clone.c             |   25 ++++++++++++++++++---\n t/t5706-clone-branch.sh     |   49 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 75 insertions(+), 4 deletions(-)\n create mode 100755 t/t5706-clone-branch.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 2c63a0f..5cd106c 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -127,6 +127,11 @@ objects from the source repository into a pack in the cloned repository.\n \tInstead of using the remote name 'origin' to keep track\n \tof the upstream repository, use <name>.\n \n+--branch <name>::\n+-b <name>::\n+\tCreate a local branch head for <name> instead of the branch\n+\treferenced by the remote repos HEAD.\n+\n --upload-pack <upload-pack>::\n -u <upload-pack>::\n \tWhen given, and the repository to clone from is accessed\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 32dea74..91392a3 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n static char *option_template, *option_reference, *option_depth;\n static char *option_origin = NULL;\n+static char *option_branch = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n \n@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {\n \t\t   \"reference repository\"),\n \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n+\tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n+\t\t   \"use <branch> from upstream as HEAD\"),\n \tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n \t\t   \"path to git-upload-pack on the remote\"),\n \tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n@@ -347,8 +350,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir;\n \tint dest_exists;\n-\tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n-\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n+\tconst struct ref *refs, *head_points_at, *remote_head = NULL, *mapped_refs;\n+\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT, branch_head = STRBUF_INIT;\n \tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tchar *src_ref_prefix = \"refs/heads/\";\n@@ -518,8 +521,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n \n-\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n-\t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n+\t\tif (option_branch) {\n+\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n+\t\t}\n+\n+\t\tif (!remote_head) {\n+\t\t\tif (option_branch)\n+\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n+\t\t\t\t\t\", using HEAD instead\",\n+\t\t\t\t\toption_branch, option_origin);\n+\n+\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n+\t\t}\n+\n+\t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 1);\n \t}\n \telse {\n \t\twarning(\"You appear to have cloned an empty repository.\");\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nnew file mode 100755\nindex 0000000..b5fec50\n--- /dev/null\n+++ b/t/t5706-clone-branch.sh\n@@ -0,0 +1,49 @@\n+#!/bin/sh\n+\n+test_description='branch clone options'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tmkdir parent &&\n+\t(cd parent && git init &&\n+\t echo one >file && git add file &&\n+\t git commit -m one && git branch foo &&\n+\t git checkout -b two &&\n+\t echo two >f && git add f && git commit -m two &&\n+\t git checkout master)\n+\n+'\n+\n+test_expect_success 'clone' '\n+\n+\tgit clone parent clone &&\n+\t(cd clone &&\n+\ttest $(git rev-parse --verify HEAD) = \\\n+\t     $(git rev-parse --verify refs/remotes/origin/master) &&\n+\ttest $(git rev-parse --verify HEAD) != \\\n+\t     $(git rev-parse --verify refs/remotes/origin/two))\n+\n+\n+'\n+\n+test_expect_success 'clone -b two' '\n+\n+\tgit clone -b two parent clone-b &&\n+\t(cd clone-b &&\n+\ttest $(git rev-parse --verify HEAD) = \\\n+\t     $(git rev-parse --verify refs/remotes/origin/two) &&\n+\ttest $(git rev-parse --verify HEAD) != \\\n+\t     $(git rev-parse --verify refs/remotes/origin/master))\n+\n+'\n+\n+test_expect_success 'clone -b foo' '\n+\n+\tgit clone -b foo parent clone-b-foo &&\n+\t(cd clone-b-foo &&\n+\ttest $(git branch | grep \\* | sed -e s:\\*\\ ::) = foo)\n+\n+'\n+\n+test_done\n-- \n1.6.2\n"},{"id":"121808","messageId":"20090826155029.GA5750@atjola.homenet","threadId":"20733","inReplyTo":"1251298007-18693-1-git-send-email-catap@catap.ru","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Björn Steinbrink","fromEmail":"b.steinbrink@gmx.de","sentAt":"2009-08-26T15:50:29Z","receivedAt":"2009-08-26T15:50:29Z","isPatch":true,"sender":{"key":"b.steinbrink@gmx.de","avatar":"https://avatars.githubusercontent.com/u/230962?v=4"},"body":"On 2009.08.26 18:46:47 +0400, Kirill A. Korinskiy wrote:\n> @@ -518,8 +521,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n>  \n> -\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> -\t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n> +\t\tif (option_branch) {\n> +\t\t\tstrbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n> +\n> +\t\t\tremote_head = find_ref_by_name(refs, branch_head.buf);\n> +\t\t}\n> +\n> +\t\tif (!remote_head) {\n> +\t\t\tif (option_branch)\n> +\t\t\t\twarning(\"Remote branch %s not found in upstream %s\"\n> +\t\t\t\t\t\", using HEAD instead\",\n> +\t\t\t\t\toption_branch, option_origin);\n> +\n> +\t\t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n> +\t\t}\n> +\n> +\t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 1);\n\nJust setting \"all\" to 1 there is wrong. With \"all\" set to 1,\nguess_remote_head() returns a linked list of _all_ matching refs. The\nfirst entry in that list depends on the order of mapped_refs.\n\ndoener@atjola:h $ mkdir a; cd a; git init\nInitialized empty Git repository in /home/doener/h/a/.git/\ndoener@atjola:a (master) $ git commit --allow-empty -m init\n[master (root-commit) aa39247] init\ndoener@atjola:a (master) $ git branch foo\ndoener@atjola:a (master) $ cd ..\n\ndoener@atjola:h $ (git clone -b foo a foo; cd foo; git branch)\nInitialized empty Git repository in /home/doener/h/foo/.git/\n* foo\n\ndoener@atjola:h $ (git clone -b master a master; cd master; git branch)\nInitialized empty Git repository in /home/doener/h/master/.git/\n* foo\n\nHere, \"foo\" was first in mapped_refs, and so \"-b master\" used that, too.\n\nUsing guess_remote_head() seems pretty wrong. With -b given, you don't\nwant to guess anymore, you _know_ which one you want. Unfortunately, I\ndon't see a straight-forward way to handle that (but I'm totally\nclueless about the code, so don't let me scare you ;-)).\n\n> diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\n> new file mode 100755\n> index 0000000..b5fec50\n> --- /dev/null\n> +++ b/t/t5706-clone-branch.sh\n> @@ -0,0 +1,49 @@\n> +#!/bin/sh\n> +\n> +test_description='branch clone options'\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\n> +\tmkdir parent &&\n> +\t(cd parent && git init &&\n> +\t echo one >file && git add file &&\n> +\t git commit -m one && git branch foo &&\n> +\t git checkout -b two &&\n> +\t echo two >f && git add f && git commit -m two &&\n> +\t git checkout master)\n> +\n> +'\n> +\n> +test_expect_success 'clone' '\n> +\n> +\tgit clone parent clone &&\n> +\t(cd clone &&\n> +\ttest $(git rev-parse --verify HEAD) = \\\n> +\t     $(git rev-parse --verify refs/remotes/origin/master) &&\n> +\ttest $(git rev-parse --verify HEAD) != \\\n> +\t     $(git rev-parse --verify refs/remotes/origin/two))\n> +\n> +\n> +'\n> +\n> +test_expect_success 'clone -b two' '\n> +\n> +\tgit clone -b two parent clone-b &&\n> +\t(cd clone-b &&\n> +\ttest $(git rev-parse --verify HEAD) = \\\n> +\t     $(git rev-parse --verify refs/remotes/origin/two) &&\n> +\ttest $(git rev-parse --verify HEAD) != \\\n> +\t     $(git rev-parse --verify refs/remotes/origin/master))\n> +\n> +'\n> +\n> +test_expect_success 'clone -b foo' '\n> +\n> +\tgit clone -b foo parent clone-b-foo &&\n> +\t(cd clone-b-foo &&\n> +\ttest $(git branch | grep \\* | sed -e s:\\*\\ ::) = foo)\n\nThis should probably do \"git symbolic-ref HEAD\" instead of the branch +\ngrep + sed. And it should also verify that rev-parse foo == rev-parse\norigin/foo.\n\nAnd to catch the above bug, you need a second test like that, but for\n\"master\" instead of \"foo\".\n\nHTH\nBjörn\n"},{"id":"121810","messageId":"20090826161059.GC32741@coredump.intra.peff.net","threadId":"20733","inReplyTo":"20090826155029.GA5750@atjola.homenet","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-08-26T16:10:59Z","receivedAt":"2009-08-26T16:10:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 26, 2009 at 05:50:29PM +0200, Björn Steinbrink wrote:\n\n> Using guess_remote_head() seems pretty wrong. With -b given, you don't\n> want to guess anymore, you _know_ which one you want. Unfortunately, I\n> don't see a straight-forward way to handle that (but I'm totally\n> clueless about the code, so don't let me scare you ;-)).\n\nThanks for pointing this out, Björn (I really should have noticed it on\nfirst review, but I guess many eyes, shallow bugs, etc. :) ).\n\nThis code is a little bit confusing, so let me explain:\n\n  - we look up the remote HEAD, getting its commit sha1. If the protocol\n    supports it, we also get its symref information.\n\n  - we then pass the result to guess_remote_head. _If_ we have symref\n    information, then we can quit immediately, as the symref contains\n    what we want. If it doesn't, then we proceed with trying to match up\n    the commit sha1 with one of the other refs.\n\nSo if you want to create a \"remote_head\" object via \"-b\" which acts as\nif it was the remote HEAD, you would need to actually create a new ref\nobject and set the \"symref\" field appropriately.\n\nBut I don't think there is any need to do that here. We simply want to\navoid calling guess_remote_head at all, since we know there is nothing\nto guess at. We do still need to know whether remote_head is non-NULL\nlater, though.\n\nSo I think the code should probably look like this (totally untested):\n\n  remote_head = find_ref_by_name(refs, \"HEAD\");\n  if (option_branch) {\n          strbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n          head_points_at = find_ref_by_name(refs, branch_head.buf);\n          if (!head_points_at)\n                  warning(\"remote branch not found, etc\");\n  }\n  if (!head_points_at)\n    head_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n\nand then initialize head_points_at to NULL instead of remote_head.\n\n-Peff\n"},{"id":"121811","messageId":"20090826165618.GA7477@atjola.homenet","threadId":"20733","inReplyTo":"20090826161059.GC32741@coredump.intra.peff.net","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Björn Steinbrink","fromEmail":"b.steinbrink@gmx.de","sentAt":"2009-08-26T16:56:18Z","receivedAt":"2009-08-26T16:56:18Z","isPatch":true,"sender":{"key":"b.steinbrink@gmx.de","avatar":"https://avatars.githubusercontent.com/u/230962?v=4"},"body":"On 2009.08.26 12:10:59 -0400, Jeff King wrote:\n> So I think the code should probably look like this (totally untested):\n> \n>   remote_head = find_ref_by_name(refs, \"HEAD\");\n>   if (option_branch) {\n>           strbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n>           head_points_at = find_ref_by_name(refs, branch_head.buf);\n>           if (!head_points_at)\n>                   warning(\"remote branch not found, etc\");\n>   }\n>   if (!head_points_at)\n>     head_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n\nHm, why \"refs\" for find_ref_by_name(), but \"mapped_ref\" for\nguess_remote_head()?\n\nBjörn\n"},{"id":"121815","messageId":"20090826174823.GA1202@coredump.intra.peff.net","threadId":"20733","inReplyTo":"20090826165618.GA7477@atjola.homenet","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-08-26T17:48:23Z","receivedAt":"2009-08-26T17:48:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 26, 2009 at 06:56:18PM +0200, Björn Steinbrink wrote:\n\n> On 2009.08.26 12:10:59 -0400, Jeff King wrote:\n> > So I think the code should probably look like this (totally untested):\n> > \n> >   remote_head = find_ref_by_name(refs, \"HEAD\");\n> >   if (option_branch) {\n> >           strbuf_addf(&branch_head, \"%s%s\", src_ref_prefix, option_branch);\n> >           head_points_at = find_ref_by_name(refs, branch_head.buf);\n> >           if (!head_points_at)\n> >                   warning(\"remote branch not found, etc\");\n> >   }\n> >   if (!head_points_at)\n> >     head_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n> \n> Hm, why \"refs\" for find_ref_by_name(), but \"mapped_ref\" for\n> guess_remote_head()?\n\nBlind copying of the current code? :)\n\nGood question, though. AFAICT, the difference between mapped_refs and\nrefs is that the former contains only the refs we are actually fetching,\nand its peer_ref member is filled in as appropriate.\n\nLater in the code, we look at head_points_at->peer_ref, which means it\n_must_ come from mapped_refs. And which means the code I posted is\nbogus, as the ref we look up in \"refs\" will not have that member filled\nin. So I think we do need:\n\n  head_points_at = find_ref_by_name(mapped_refs, branch_head.buf);\n\nGood catch.\n\n-Peff\n"},{"id":"121818","messageId":"20090826190508.GA2863@coredump.intra.peff.net","threadId":"20733","inReplyTo":"20090826174823.GA1202@coredump.intra.peff.net","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2009-08-26T19:05:08Z","receivedAt":"2009-08-26T19:05:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 26, 2009 at 01:48:23PM -0400, Jeff King wrote:\n\n> Later in the code, we look at head_points_at->peer_ref, which means it\n> _must_ come from mapped_refs. And which means the code I posted is\n> bogus, as the ref we look up in \"refs\" will not have that member filled\n> in. So I think we do need:\n> \n>   head_points_at = find_ref_by_name(mapped_refs, branch_head.buf);\n\nActually, it is much more complicated than that. We want to do several\nthings with the remote HEAD:\n\n  1. set up our HEAD; this uses head_points_at now\n\n  2. set up a pointer in refs/remotes/$origin/HEAD; this uses\n     head_points_at now\n\n  3. check out the actual contents; this uses remote_head (and it can't\n     just blindly use head_points_at, because it may be a detached HEAD).\n\nSo you can see by (1) and (2) that we actually need to distinguish\nbetween the remote's HEAD and where we want our HEAD to be. And we need\nto checkout _our_ HEAD, if available, falling back to the remote's head.\n\nI really wish clone was implemented simply as \"init && remote add &&\nfetch && checkout\", which would have made this a lot easier. We would\nsimply be munging the HEAD file in the middle.\n\nAnyway, here is a patch which I think does the right thing, and tests\neach of the desired behaviors in the test script. If there are further\n\"should it do X or Y\" questions, please phrase them in the form of a\npatch to the test script. ;)\n\nI also tried to roll in comments on the documentation to make it a bit\nclearer.\n\n-- >8 --\nSubject: [PATCH] clone: add --branch option to select a different HEAD\n\nWe currently point the HEAD of a newly cloned repo to the\nsame ref as the parent repo's HEAD. While a user can then\n\"git checkout -b foo origin/foo\" whichever branch they\nchoose, it is more convenient and more efficient to tell\nclone which branch you want in the first place.\n\nBased on a patch by Kirill A. Korinskiy.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-clone.txt |    7 ++++\n builtin-clone.c             |   75 +++++++++++++++++++++++++++----------------\n t/t5706-clone-branch.sh     |   68 +++++++++++++++++++++++++++++++++++++++\n 3 files changed, 122 insertions(+), 28 deletions(-)\n create mode 100755 t/t5706-clone-branch.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 2c63a0f..1cd1ecc 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -127,6 +127,13 @@ objects from the source repository into a pack in the cloned repository.\n \tInstead of using the remote name 'origin' to keep track\n \tof the upstream repository, use <name>.\n \n+--branch <name>::\n+-b <name>::\n+\tInstead of pointing the newly created HEAD to the branch pointed\n+\tto by the cloned repositoroy's HEAD, point to <name> branch\n+\tinstead. In a non-bare repository, this is the branch that will\n+\tbe checked out.\n+\n --upload-pack <upload-pack>::\n -u <upload-pack>::\n \tWhen given, and the repository to clone from is accessed\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 32dea74..9d79301 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n static char *option_template, *option_reference, *option_depth;\n static char *option_origin = NULL;\n+static char *option_branch = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n \n@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {\n \t\t   \"reference repository\"),\n \tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n \t\t   \"use <branch> instead of 'origin' to track upstream\"),\n+\tOPT_STRING('b', \"branch\", &option_branch, \"branch\",\n+\t\t   \"checkout <branch> instead of the remote's HEAD\"),\n \tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n \t\t   \"path to git-upload-pack on the remote\"),\n \tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n@@ -347,7 +350,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir;\n \tint dest_exists;\n-\tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n+\tconst struct ref *refs, *remote_head, *mapped_refs;\n+\tconst struct ref *remote_head_points_at;\n+\tconst struct ref *our_head_points_at;\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@@ -519,11 +524,31 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tmapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);\n \n \t\tremote_head = find_ref_by_name(refs, \"HEAD\");\n-\t\thead_points_at = guess_remote_head(remote_head, mapped_refs, 0);\n+\t\tremote_head_points_at =\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+\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}\n+\t\telse\n+\t\t\tour_head_points_at = remote_head_points_at;\n \t}\n \telse {\n \t\twarning(\"You appear to have cloned an empty repository.\");\n-\t\thead_points_at = NULL;\n+\t\tour_head_points_at = NULL;\n+\t\tremote_head_points_at = NULL;\n \t\tremote_head = NULL;\n \t\toption_no_checkout = 1;\n \t\tif (!option_bare)\n@@ -531,41 +556,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n-\tif (head_points_at) {\n-\t\t/* Local default branch link */\n-\t\tcreate_symref(\"HEAD\", head_points_at->name, NULL);\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 \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\tstruct strbuf head_ref = STRBUF_INIT;\n-\t\t\tconst char *head = head_points_at->name;\n-\n-\t\t\tif (!prefixcmp(head, \"refs/heads/\"))\n-\t\t\t\thead += 11;\n-\n-\t\t\t/* Set up the initial local branch */\n-\n-\t\t\t/* Local branch initial value */\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   head_points_at->old_sha1,\n+\t\t\t\t   our_head_points_at->old_sha1,\n \t\t\t\t   NULL, 0, DIE_ON_ERR);\n-\n-\t\t\tstrbuf_addstr(&head_ref, branch_top.buf);\n-\t\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n-\n-\t\t\t/* Remote branch link */\n-\t\t\tcreate_symref(head_ref.buf,\n-\t\t\t\t      head_points_at->peer_ref->name,\n-\t\t\t\t      reflog_msg.buf);\n-\n \t\t\tinstall_branch_config(0, head, option_origin,\n-\t\t\t\t\t      head_points_at->name);\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\tif (!option_bare)\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} else {\n \t\t/* Nothing to checkout out */\n \t\tif (!option_no_checkout)\n@@ -597,7 +616,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\topts.src_index = &the_index;\n \t\topts.dst_index = &the_index;\n \n-\t\ttree = parse_tree_indirect(remote_head->old_sha1);\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);\ndiff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh\nnew file mode 100755\nindex 0000000..f3f9a76\n--- /dev/null\n+++ b/t/t5706-clone-branch.sh\n@@ -0,0 +1,68 @@\n+#!/bin/sh\n+\n+test_description='clone --branch option'\n+. ./test-lib.sh\n+\n+check_HEAD() {\n+\techo refs/heads/\"$1\" >expect &&\n+\tgit symbolic-ref HEAD >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+check_file() {\n+\techo \"$1\" >expect &&\n+\ttest_cmp expect file\n+}\n+\n+test_expect_success 'setup' '\n+\tmkdir parent &&\n+\t(cd parent && git init &&\n+\t echo one >file && git add file && git commit -m one &&\n+\t git checkout -b two &&\n+\t echo two >file && git add file && git commit -m two &&\n+\t git checkout master)\n+'\n+\n+test_expect_success 'vanilla clone chooses HEAD' '\n+\tgit clone parent clone &&\n+\t(cd clone &&\n+\t check_HEAD master &&\n+\t check_file one\n+\t)\n+'\n+\n+test_expect_success 'clone -b chooses specified branch' '\n+\tgit clone -b two parent clone-two &&\n+\t(cd clone-two &&\n+\t check_HEAD two &&\n+\t check_file two\n+\t)\n+'\n+\n+test_expect_success 'clone -b sets up tracking' '\n+\t(cd clone-two &&\n+\t echo origin >expect &&\n+\t git config branch.two.remote >actual &&\n+\t echo refs/heads/two >>expect &&\n+\t git config branch.two.merge >>actual &&\n+\t test_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '\n+\t(cd clone-two &&\n+\t echo refs/remotes/origin/master >expect &&\n+\t git symbolic-ref refs/remotes/origin/HEAD >actual &&\n+\t test_cmp expect actual\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+'\n+\n+test_done\n-- \n1.6.4.1.340.ge9f66.dirty\n"},{"id":"121992","messageId":"4A97B1F3.5060408@gmail.com","threadId":"20733","inReplyTo":"1251228341-29434-1-git-send-email-catap@catap.ru","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-08-28T10:31:15Z","receivedAt":"2009-08-28T10:31:15Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"On 8/25/09 9:25 PM, Kirill A. Korinskiy wrote:\n> Sometimes (especially on production systems) we need to use only one\n> remote branch for building software. It really annoying to clone\n> origin and then swith branch by hand everytime. So this patch provide\n> functionality to clone remote branch with one command without using\n> checkout after clone.\n\nGlad to see this topic being revised. Here's the original discussion for \nreference:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/114919/\n\nTor Arne\n"},{"id":"121994","messageId":"46a038f90908280405o1e86bf4fo60eb07957ce9158a@mail.gmail.com","threadId":"20733","inReplyTo":"4A97B1F3.5060408@gmail.com","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2009-08-28T11:05:22Z","receivedAt":"2009-08-28T11:05:22Z","isPatch":true,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On Fri, Aug 28, 2009 at 12:31 PM, Tor Arne Vestbø<torarnv@gmail.com> wrote:\n> Glad to see this topic being revised. Here's the original discussion for\n> reference:\n\nISTR that early git supported this -- maybe just on the HTTP\nimplementation? I sure remember writing\n\n   git clone http://host/foo.git#branchname\n\nand getting a cloned repo where 'branchname' was tracking\n'origin/branchname', and was checked out as HEAD.\n\nIt would have been dropped by the switch to C builtins :-/\n\ncheers,\n\n\n\nm\n-- \n martin.langhoff@gmail.com\n martin@laptop.org -- School Server Architect\n - ask interesting questions\n - don't get distracted with shiny stuff  - working code first\n - http://wiki.laptop.org/go/User:Martinlanghoff\n"},{"id":"121997","messageId":"alpine.LNX.2.00.0908281238350.18415@reaper.quantumfyre.co.uk","threadId":"20733","inReplyTo":"46a038f90908280405o1e86bf4fo60eb07957ce9158a@mail.gmail.com","subject":"Re: [PATCH] Add option -b/--branch to clone for select a new HEAD","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2009-08-28T12:10:02Z","receivedAt":"2009-08-28T12:10:02Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Fri, 28 Aug 2009, Martin Langhoff wrote:\n\n> On Fri, Aug 28, 2009 at 12:31 PM, Tor Arne Vestb?<torarnv@gmail.com> wrote:\n>> Glad to see this topic being revised. Here's the original discussion for\n>> reference:\n>\n> ISTR that early git supported this -- maybe just on the HTTP\n> implementation? I sure remember writing\n>\n>   git clone http://host/foo.git#branchname\n>\n> and getting a cloned repo where 'branchname' was tracking\n> 'origin/branchname', and was checked out as HEAD.\n\nDid you use cogito perhaps?  That had #branch syntax IIRC ...\n\n> It would have been dropped by the switch to C builtins :-/\n>\n> cheers,\n>\n>\n>\n> m\n>\n\n-- \nJulian\n\n  ---\n\"What duck?\"\n         -- (Terry Pratchett, Soul Music)\n"}]}