Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

7 messages, 3 authors, 2016-06-15 · open the first message on its own page

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Björn Steinbrink <hidden>
Date: 2016-06-15 22:47:19

On 2009.08.26 15:53:58 +0400, Kirill A. Korinskiy wrote:
At Wed, 26 Aug 2009 00:36:37 +0200,
Björn Steinbrink [off-list ref] wrote:

quoted
quoted
@@ -518,7 +521,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
-		remote_head = find_ref_by_name(refs, "HEAD");
+		if (option_branch) {
+			strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
+
+			remote_head = find_ref_by_name(refs, branch_head.buf);
+		}
+
+		if (!remote_head) {
+			if (option_branch)
+				warning("Remote branch %s not found in upstream %s"
+					", using HEAD instead",
+					option_branch, option_origin);
+
+			remote_head = find_ref_by_name(refs, "HEAD");
+		}
+
 		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
This would still pick refs/heads/master if refs/heads/master and
refs/heads/<branch> reference the same commit. That's due to the check
in guess_remote_head() which prefers refs/heads/master over all other
refs. While this is acceptable for the HEAD lookup, I'd treat that as a
bug for this new option.
My english is not a good and I don't understand it, sorry.
guess_remote_head() compares the object ids from remote_head and all of
the remote's refs to guess which is the right one.

Let's say that the repo has:

refs/heads/master: object1
refs/heads/foo: object2
refs/heads/bar: object1

If you do "git clone -b foo ...", then remote_head->old_sha1 will be
"object2". guess_remote_head() compares that to all the remote heads. In
this case, it will find refs/heads/foo (as expected).

But when you do "git clone -b bar", then remote_head->old_sha1 will be
"object1". And guess_remote_head() will then take refs/heads/master,
as it prefers that one.

doener@atjola:h $ mkdir a; cd a; git init
Initialized empty Git repository in /home/doener/h/a/.git/
doener@atjola:a (master) $ git commit --allow-empty -m init
[master (root-commit) a7a0b54] init
doener@atjola:a (master) $ git branch bar
doener@atjola:a (master) $ git checkout -b foo
Switched to a new branch 'foo'
doener@atjola:a (foo) $ git commit --allow-empty -m on_foo
[foo 375047e] on_foo
doener@atjola:a (foo) $ cd ..
doener@atjola:h $ (git clone -b foo a foo; cd foo; git branch)
Initialized empty Git repository in /home/doener/h/foo/.git/
* foo
doener@atjola:h $ (git clone -b bar a bar; cd bar; git branch)
Initialized empty Git repository in /home/doener/h/bar/.git/
* master


That said, I actually wonder why you don't simple set HEAD in the
original repo so that you get whichever branch you want by default
anyway.

Björn

[PATCH] Add option -b/--branch to clone for select a new HEAD

From: Kirill A. Korinskiy <hidden>
Date: 2016-06-15 22:47:19

Sometimes (especially on production systems) we need to use only one
remote branch for building software. It's really annoying to clone
origin and then switch branch by hand everytime. So this patch
provides functionality to clone a remote branch with one command
without using checkout after clone.

Signed-off-by: Kirill A. Korinskiy <redacted>
---
 Documentation/git-clone.txt |    5 ++++
 builtin-clone.c             |   25 ++++++++++++++++++---
 t/t5706-clone-branch.sh     |   49 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+), 4 deletions(-)
 create mode 100755 t/t5706-clone-branch.sh
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 2c63a0f..5cd106c 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -127,6 +127,11 @@ objects from the source repository into a pack in the cloned repository.
 	Instead of using the remote name 'origin' to keep track
 	of the upstream repository, use <name>.
 
+--branch <name>::
+-b <name>::
+	Create a local branch head for <name> instead of the branch
+	referenced by the remote repos HEAD.
+
 --upload-pack <upload-pack>::
 -u <upload-pack>::
 	When given, and the repository to clone from is accessed
diff --git a/builtin-clone.c b/builtin-clone.c
index 32dea74..91392a3 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;
 static int option_local, option_no_hardlinks, option_shared;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
+static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbose;
 
@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {
 		   "reference repository"),
 	OPT_STRING('o', "origin", &option_origin, "branch",
 		   "use <branch> instead of 'origin' to track upstream"),
+	OPT_STRING('b', "branch", &option_branch, "branch",
+		   "use <branch> from upstream as HEAD"),
 	OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
 		   "path to git-upload-pack on the remote"),
 	OPT_STRING(0, "depth", &option_depth, "depth",
@@ -347,8 +350,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	const char *repo_name, *repo, *work_tree, *git_dir;
 	char *path, *dir;
 	int dest_exists;
-	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
-	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
+	const struct ref *refs, *head_points_at, *remote_head = NULL, *mapped_refs;
+	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT, branch_head = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
 	char *src_ref_prefix = "refs/heads/";
@@ -518,8 +521,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
-		remote_head = find_ref_by_name(refs, "HEAD");
-		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+		if (option_branch) {
+			strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
+
+			remote_head = find_ref_by_name(refs, branch_head.buf);
+		}
+
+		if (!remote_head) {
+			if (option_branch)
+				warning("Remote branch %s not found in upstream %s"
+					", using HEAD instead",
+					option_branch, option_origin);
+
+			remote_head = find_ref_by_name(refs, "HEAD");
+		}
+
+		head_points_at = guess_remote_head(remote_head, mapped_refs, 1);
 	}
 	else {
 		warning("You appear to have cloned an empty repository.");
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 0000000..b5fec50
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='branch clone options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	mkdir parent &&
+	(cd parent && git init &&
+	 echo one >file && git add file &&
+	 git commit -m one && git branch foo &&
+	 git checkout -b two &&
+	 echo two >f && git add f && git commit -m two &&
+	 git checkout master)
+
+'
+
+test_expect_success 'clone' '
+
+	git clone parent clone &&
+	(cd clone &&
+	test $(git rev-parse --verify HEAD) = \
+	     $(git rev-parse --verify refs/remotes/origin/master) &&
+	test $(git rev-parse --verify HEAD) != \
+	     $(git rev-parse --verify refs/remotes/origin/two))
+
+
+'
+
+test_expect_success 'clone -b two' '
+
+	git clone -b two parent clone-b &&
+	(cd clone-b &&
+	test $(git rev-parse --verify HEAD) = \
+	     $(git rev-parse --verify refs/remotes/origin/two) &&
+	test $(git rev-parse --verify HEAD) != \
+	     $(git rev-parse --verify refs/remotes/origin/master))
+
+'
+
+test_expect_success 'clone -b foo' '
+
+	git clone -b foo parent clone-b-foo &&
+	(cd clone-b-foo &&
+	test $(git branch | grep \* | sed -e s:\*\ ::) = foo)
+
+'
+
+test_done
-- 
1.6.2

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Björn Steinbrink <hidden>
Date: 2016-06-15 22:47:19

On 2009.08.26 18:46:47 +0400, Kirill A. Korinskiy wrote:
quoted hunk
@@ -518,8 +521,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
-		remote_head = find_ref_by_name(refs, "HEAD");
-		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+		if (option_branch) {
+			strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
+
+			remote_head = find_ref_by_name(refs, branch_head.buf);
+		}
+
+		if (!remote_head) {
+			if (option_branch)
+				warning("Remote branch %s not found in upstream %s"
+					", using HEAD instead",
+					option_branch, option_origin);
+
+			remote_head = find_ref_by_name(refs, "HEAD");
+		}
+
+		head_points_at = guess_remote_head(remote_head, mapped_refs, 1);
Just setting "all" to 1 there is wrong. With "all" set to 1,
guess_remote_head() returns a linked list of _all_ matching refs. The
first entry in that list depends on the order of mapped_refs.

doener@atjola:h $ mkdir a; cd a; git init
Initialized empty Git repository in /home/doener/h/a/.git/
doener@atjola:a (master) $ git commit --allow-empty -m init
[master (root-commit) aa39247] init
doener@atjola:a (master) $ git branch foo
doener@atjola:a (master) $ cd ..

doener@atjola:h $ (git clone -b foo a foo; cd foo; git branch)
Initialized empty Git repository in /home/doener/h/foo/.git/
* foo

doener@atjola:h $ (git clone -b master a master; cd master; git branch)
Initialized empty Git repository in /home/doener/h/master/.git/
* foo

Here, "foo" was first in mapped_refs, and so "-b master" used that, too.

Using guess_remote_head() seems pretty wrong. With -b given, you don't
want to guess anymore, you _know_ which one you want. Unfortunately, I
don't see a straight-forward way to handle that (but I'm totally
clueless about the code, so don't let me scare you ;-)).
quoted hunk
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 0000000..b5fec50
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='branch clone options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	mkdir parent &&
+	(cd parent && git init &&
+	 echo one >file && git add file &&
+	 git commit -m one && git branch foo &&
+	 git checkout -b two &&
+	 echo two >f && git add f && git commit -m two &&
+	 git checkout master)
+
+'
+
+test_expect_success 'clone' '
+
+	git clone parent clone &&
+	(cd clone &&
+	test $(git rev-parse --verify HEAD) = \
+	     $(git rev-parse --verify refs/remotes/origin/master) &&
+	test $(git rev-parse --verify HEAD) != \
+	     $(git rev-parse --verify refs/remotes/origin/two))
+
+
+'
+
+test_expect_success 'clone -b two' '
+
+	git clone -b two parent clone-b &&
+	(cd clone-b &&
+	test $(git rev-parse --verify HEAD) = \
+	     $(git rev-parse --verify refs/remotes/origin/two) &&
+	test $(git rev-parse --verify HEAD) != \
+	     $(git rev-parse --verify refs/remotes/origin/master))
+
+'
+
+test_expect_success 'clone -b foo' '
+
+	git clone -b foo parent clone-b-foo &&
+	(cd clone-b-foo &&
+	test $(git branch | grep \* | sed -e s:\*\ ::) = foo)
This should probably do "git symbolic-ref HEAD" instead of the branch +
grep + sed. And it should also verify that rev-parse foo == rev-parse
origin/foo.

And to catch the above bug, you need a second test like that, but for
"master" instead of "foo".

HTH
Björn

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Jeff King <hidden>
Date: 2016-06-15 22:47:19

On Wed, Aug 26, 2009 at 05:50:29PM +0200, Björn Steinbrink wrote:
Using guess_remote_head() seems pretty wrong. With -b given, you don't
want to guess anymore, you _know_ which one you want. Unfortunately, I
don't see a straight-forward way to handle that (but I'm totally
clueless about the code, so don't let me scare you ;-)).
Thanks for pointing this out, Björn (I really should have noticed it on
first review, but I guess many eyes, shallow bugs, etc. :) ).

This code is a little bit confusing, so let me explain:

  - we look up the remote HEAD, getting its commit sha1. If the protocol
    supports it, we also get its symref information.

  - we then pass the result to guess_remote_head. _If_ we have symref
    information, then we can quit immediately, as the symref contains
    what we want. If it doesn't, then we proceed with trying to match up
    the commit sha1 with one of the other refs.

So if you want to create a "remote_head" object via "-b" which acts as
if it was the remote HEAD, you would need to actually create a new ref
object and set the "symref" field appropriately.

But I don't think there is any need to do that here. We simply want to
avoid calling guess_remote_head at all, since we know there is nothing
to guess at. We do still need to know whether remote_head is non-NULL
later, though.

So I think the code should probably look like this (totally untested):

  remote_head = find_ref_by_name(refs, "HEAD");
  if (option_branch) {
          strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
          head_points_at = find_ref_by_name(refs, branch_head.buf);
          if (!head_points_at)
                  warning("remote branch not found, etc");
  }
  if (!head_points_at)
    head_points_at = guess_remote_head(remote_head, mapped_refs, 0);

and then initialize head_points_at to NULL instead of remote_head.

-Peff

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Björn Steinbrink <hidden>
Date: 2016-06-15 22:47:19

On 2009.08.26 12:10:59 -0400, Jeff King wrote:
So I think the code should probably look like this (totally untested):

  remote_head = find_ref_by_name(refs, "HEAD");
  if (option_branch) {
          strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
          head_points_at = find_ref_by_name(refs, branch_head.buf);
          if (!head_points_at)
                  warning("remote branch not found, etc");
  }
  if (!head_points_at)
    head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
Hm, why "refs" for find_ref_by_name(), but "mapped_ref" for
guess_remote_head()?

Björn

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Jeff King <hidden>
Date: 2016-06-15 22:47:19

On Wed, Aug 26, 2009 at 06:56:18PM +0200, Björn Steinbrink wrote:
On 2009.08.26 12:10:59 -0400, Jeff King wrote:
quoted
So I think the code should probably look like this (totally untested):

  remote_head = find_ref_by_name(refs, "HEAD");
  if (option_branch) {
          strbuf_addf(&branch_head, "%s%s", src_ref_prefix, option_branch);
          head_points_at = find_ref_by_name(refs, branch_head.buf);
          if (!head_points_at)
                  warning("remote branch not found, etc");
  }
  if (!head_points_at)
    head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
Hm, why "refs" for find_ref_by_name(), but "mapped_ref" for
guess_remote_head()?
Blind copying of the current code? :)

Good question, though. AFAICT, the difference between mapped_refs and
refs is that the former contains only the refs we are actually fetching,
and its peer_ref member is filled in as appropriate.

Later in the code, we look at head_points_at->peer_ref, which means it
_must_ come from mapped_refs. And which means the code I posted is
bogus, as the ref we look up in "refs" will not have that member filled
in. So I think we do need:

  head_points_at = find_ref_by_name(mapped_refs, branch_head.buf);

Good catch.

-Peff

Re: [PATCH] Add option -b/--branch to clone for select a new HEAD

From: Jeff King <hidden>
Date: 2016-06-15 22:47:19

On Wed, Aug 26, 2009 at 01:48:23PM -0400, Jeff King wrote:
Later in the code, we look at head_points_at->peer_ref, which means it
_must_ come from mapped_refs. And which means the code I posted is
bogus, as the ref we look up in "refs" will not have that member filled
in. So I think we do need:

  head_points_at = find_ref_by_name(mapped_refs, branch_head.buf);
Actually, it is much more complicated than that. We want to do several
things with the remote HEAD:

  1. set up our HEAD; this uses head_points_at now

  2. set up a pointer in refs/remotes/$origin/HEAD; this uses
     head_points_at now

  3. check out the actual contents; this uses remote_head (and it can't
     just blindly use head_points_at, because it may be a detached HEAD).

So you can see by (1) and (2) that we actually need to distinguish
between the remote's HEAD and where we want our HEAD to be. And we need
to checkout _our_ HEAD, if available, falling back to the remote's head.

I really wish clone was implemented simply as "init && remote add &&
fetch && checkout", which would have made this a lot easier. We would
simply be munging the HEAD file in the middle.

Anyway, here is a patch which I think does the right thing, and tests
each of the desired behaviors in the test script. If there are further
"should it do X or Y" questions, please phrase them in the form of a
patch to the test script. ;)

I also tried to roll in comments on the documentation to make it a bit
clearer.

-- >8 --
Subject: [PATCH] clone: add --branch option to select a different HEAD

We currently point the HEAD of a newly cloned repo to the
same ref as the parent repo's HEAD. While a user can then
"git checkout -b foo origin/foo" whichever branch they
choose, it is more convenient and more efficient to tell
clone which branch you want in the first place.

Based on a patch by Kirill A. Korinskiy.

Signed-off-by: Jeff King <redacted>
---
 Documentation/git-clone.txt |    7 ++++
 builtin-clone.c             |   75 +++++++++++++++++++++++++++----------------
 t/t5706-clone-branch.sh     |   68 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+), 28 deletions(-)
 create mode 100755 t/t5706-clone-branch.sh
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 2c63a0f..1cd1ecc 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -127,6 +127,13 @@ objects from the source repository into a pack in the cloned repository.
 	Instead of using the remote name 'origin' to keep track
 	of the upstream repository, use <name>.
 
+--branch <name>::
+-b <name>::
+	Instead of pointing the newly created HEAD to the branch pointed
+	to by the cloned repositoroy's HEAD, point to <name> branch
+	instead. In a non-bare repository, this is the branch that will
+	be checked out.
+
 --upload-pack <upload-pack>::
 -u <upload-pack>::
 	When given, and the repository to clone from is accessed
diff --git a/builtin-clone.c b/builtin-clone.c
index 32dea74..9d79301 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;
 static int option_local, option_no_hardlinks, option_shared;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
+static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbose;
 
@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {
 		   "reference repository"),
 	OPT_STRING('o', "origin", &option_origin, "branch",
 		   "use <branch> instead of 'origin' to track upstream"),
+	OPT_STRING('b', "branch", &option_branch, "branch",
+		   "checkout <branch> instead of the remote's HEAD"),
 	OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
 		   "path to git-upload-pack on the remote"),
 	OPT_STRING(0, "depth", &option_depth, "depth",
@@ -347,7 +350,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	const char *repo_name, *repo, *work_tree, *git_dir;
 	char *path, *dir;
 	int dest_exists;
-	const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+	const struct ref *refs, *remote_head, *mapped_refs;
+	const struct ref *remote_head_points_at;
+	const struct ref *our_head_points_at;
 	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
@@ -519,11 +524,31 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
 
 		remote_head = find_ref_by_name(refs, "HEAD");
-		head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+		remote_head_points_at =
+			guess_remote_head(remote_head, mapped_refs, 0);
+
+		if (option_branch) {
+			struct strbuf head = STRBUF_INIT;
+			strbuf_addstr(&head, src_ref_prefix);
+			strbuf_addstr(&head, option_branch);
+			our_head_points_at =
+				find_ref_by_name(mapped_refs, head.buf);
+			strbuf_release(&head);
+
+			if (!our_head_points_at) {
+				warning("Remote branch %s not found in "
+					"upstream %s, using HEAD instead",
+					option_branch, option_origin);
+				our_head_points_at = remote_head_points_at;
+			}
+		}
+		else
+			our_head_points_at = remote_head_points_at;
 	}
 	else {
 		warning("You appear to have cloned an empty repository.");
-		head_points_at = NULL;
+		our_head_points_at = NULL;
+		remote_head_points_at = NULL;
 		remote_head = NULL;
 		option_no_checkout = 1;
 		if (!option_bare)
@@ -531,41 +556,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
-	if (head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", head_points_at->name, NULL);
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top.buf);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      reflog_msg.buf);
+	}
 
+	if (our_head_points_at) {
+		/* Local default branch link */
+		create_symref("HEAD", our_head_points_at->name, NULL);
 		if (!option_bare) {
-			struct strbuf head_ref = STRBUF_INIT;
-			const char *head = head_points_at->name;
-
-			if (!prefixcmp(head, "refs/heads/"))
-				head += 11;
-
-			/* Set up the initial local branch */
-
-			/* Local branch initial value */
+			const char *head = skip_prefix(our_head_points_at->name,
+						       "refs/heads/");
 			update_ref(reflog_msg.buf, "HEAD",
-				   head_points_at->old_sha1,
+				   our_head_points_at->old_sha1,
 				   NULL, 0, DIE_ON_ERR);
-
-			strbuf_addstr(&head_ref, branch_top.buf);
-			strbuf_addstr(&head_ref, "HEAD");
-
-			/* Remote branch link */
-			create_symref(head_ref.buf,
-				      head_points_at->peer_ref->name,
-				      reflog_msg.buf);
-
 			install_branch_config(0, head, option_origin,
-					      head_points_at->name);
+					      our_head_points_at->name);
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		if (!option_bare)
+		if (!option_bare) {
 			update_ref(reflog_msg.buf, "HEAD",
 				   remote_head->old_sha1,
 				   NULL, REF_NODEREF, DIE_ON_ERR);
+			our_head_points_at = remote_head;
+		}
 	} else {
 		/* Nothing to checkout out */
 		if (!option_no_checkout)
@@ -597,7 +616,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		opts.src_index = &the_index;
 		opts.dst_index = &the_index;
 
-		tree = parse_tree_indirect(remote_head->old_sha1);
+		tree = parse_tree_indirect(our_head_points_at->old_sha1);
 		parse_tree(tree);
 		init_tree_desc(&t, tree->buffer, tree->size);
 		unpack_trees(1, &t, &opts);
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 0000000..f3f9a76
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+	echo refs/heads/"$1" >expect &&
+	git symbolic-ref HEAD >actual &&
+	test_cmp expect actual
+}
+
+check_file() {
+	echo "$1" >expect &&
+	test_cmp expect file
+}
+
+test_expect_success 'setup' '
+	mkdir parent &&
+	(cd parent && git init &&
+	 echo one >file && git add file && git commit -m one &&
+	 git checkout -b two &&
+	 echo two >file && git add file && git commit -m two &&
+	 git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+	git clone parent clone &&
+	(cd clone &&
+	 check_HEAD master &&
+	 check_file one
+	)
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+	git clone -b two parent clone-two &&
+	(cd clone-two &&
+	 check_HEAD two &&
+	 check_file two
+	)
+'
+
+test_expect_success 'clone -b sets up tracking' '
+	(cd clone-two &&
+	 echo origin >expect &&
+	 git config branch.two.remote >actual &&
+	 echo refs/heads/two >>expect &&
+	 git config branch.two.merge >>actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+	(cd clone-two &&
+	 echo refs/remotes/origin/master >expect &&
+	 git symbolic-ref refs/remotes/origin/HEAD >actual &&
+	 test_cmp expect actual
+	)
+'
+
+test_expect_success 'clone -b with bogus branch chooses HEAD' '
+	git clone -b bogus parent clone-bogus &&
+	(cd clone-bogus &&
+	 check_HEAD master &&
+	 check_file one
+	)
+'
+
+test_done
-- 
1.6.4.1.340.ge9f66.dirty
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help