git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] clone: allow detached checkout when --branch takes a tag
@ 2012-01-05 13:49 Nguyễn Thái Ngọc Duy
  2012-01-05 14:18 ` Jeff King
                   ` (2 more replies)
  0 siblings, 3 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-05 13:49 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

This allows you to do "git clone --branch=v1.7.8 git.git" and work
right away from there. No big deal, just one more convenient step, I
think. --branch taking a tag may be confusing though.

We can still have master in this case instead of detached HEAD, which
may make more sense because we use --branch. I don't care much which
way should be used.

Like? Dislike?

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   20 +++++++++++++++++++-
 1 files changed, 19 insertions(+), 1 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 8f29912..97af4bd 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -23,6 +23,7 @@
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "tag.h"
 
 /*
  * Overall FIXMEs:
@@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			strbuf_release(&head);
 
 			if (!our_head_points_at) {
+				strbuf_addstr(&head, "refs/tags/");
+				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);
@@ -750,7 +759,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
+	if (our_head_points_at &&
+	    !prefixcmp(our_head_points_at->name, "refs/tags/")) {
+		const struct ref *ref = our_head_points_at;
+		struct object *o;
+
+		/* Detached HEAD */
+		o = deref_tag(parse_object(ref->old_sha1), NULL, 0);
+		update_ref(reflog_msg.buf, "HEAD", o->sha1, NULL,
+			   REF_NODEREF, DIE_ON_ERR);
+	} else if (our_head_points_at) {
 		/* Local default branch link */
 		create_symref("HEAD", our_head_points_at->name, NULL);
 		if (!option_bare) {
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH] clone: allow detached checkout when --branch takes a tag
  2012-01-05 13:49 [PATCH] clone: allow detached checkout when --branch takes a tag Nguyễn Thái Ngọc Duy
@ 2012-01-05 14:18 ` Jeff King
  2012-01-06 11:09   ` Nguyen Thai Ngoc Duy
  2012-01-05 16:22 ` Junio C Hamano
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
  2 siblings, 1 reply; 68+ messages in thread
From: Jeff King @ 2012-01-05 14:18 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

On Thu, Jan 05, 2012 at 08:49:40PM +0700, Nguyen Thai Ngoc Duy wrote:

> This allows you to do "git clone --branch=v1.7.8 git.git" and work
> right away from there. No big deal, just one more convenient step, I
> think. --branch taking a tag may be confusing though.
> 
> We can still have master in this case instead of detached HEAD, which
> may make more sense because we use --branch. I don't care much which
> way should be used.
> 
> Like? Dislike?

Seems like a reasonable goal to me. I agree that "--branch=v1.7.8" is a
little confusing, but not the end of the world. If we were designing it
from scratch, I might call it "--head" or "--checkout" or something to
indicate that it is what we are putting in HEAD. But I don't know that
it is worth renaming the option or adding a new option.

> @@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  			strbuf_release(&head);
>  
>  			if (!our_head_points_at) {
> +				strbuf_addstr(&head, "refs/tags/");
> +				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) {

Hmm. The context just above your patch that got snipped does this:

    strbuf_addstr(&head, src_ref_prefix);
    strbuf_addstr(&head, option_branch);
    our_head_points_at =
        find_ref_by_name(mapped_refs, head.buf);

where src_ref_prefix typically is "refs/heads/", and clearly you are
meaning to do the same thing for tags. But the use of "src_ref_prefix"
is interesting.

It is always "refs/heads/" unless we are cloning into a bare mirror, in
which case it is "refs/". So with your patch in the non-mirror case,
doing "--branch=foo" would try "refs/heads/foo" followed by
"refs/tags/foo". Which makes sense. But in the mirror case, it will try
"refs/foo" followed by "refs/tags/foo", which is kind of odd.

I wonder, though, if the original code makes any sense. By using
"refs/", I would have to say "--branch=heads/foo", which is kind of
weird and undocumented. I think it should probably always be
"refs/heads/", no matter if we are mirroring or not.

> @@ -750,7 +759,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  			      reflog_msg.buf);
>  	}
>  
> -	if (our_head_points_at) {
> +	if (our_head_points_at &&
> +	    !prefixcmp(our_head_points_at->name, "refs/tags/")) {

I think I would prefer this check to be:

  prefixcmp(our_head_points_at->name, "refs/heads/")

which more closely matches the rules for what is allowed to go in HEAD
as a symbolic ref. It's pretty hard to get something other than heads or
tags, but you can do it with "git clone --bare --mirror --branch=foo/bar".
I did argue above for doing away with that "feature", but I still think
it future-proofs this section of code to handle anything.

> +		const struct ref *ref = our_head_points_at;
> +		struct object *o;
> +
> +		/* Detached HEAD */
> +		o = deref_tag(parse_object(ref->old_sha1), NULL, 0);
> +		update_ref(reflog_msg.buf, "HEAD", o->sha1, NULL,
> +			   REF_NODEREF, DIE_ON_ERR);

It's unlikely, but deref_tag can return NULL, in which case this will
segfault (ditto with parse_object, I think). I suspect that is a problem
in lots of places, though. I wonder if deref_tag should simply die if we
have a missing object (and we can add a _gently form for things like
fsck which want to handle the error condition).

Also, any reason the "warn" flag to deref_tag should not be 1?


Other than those minor complaints, the patch looks good to me.

-Peff

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH] clone: allow detached checkout when --branch takes a tag
  2012-01-05 13:49 [PATCH] clone: allow detached checkout when --branch takes a tag Nguyễn Thái Ngọc Duy
  2012-01-05 14:18 ` Jeff King
@ 2012-01-05 16:22 ` Junio C Hamano
  2012-01-06  7:35   ` Nguyen Thai Ngoc Duy
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
  2 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-05 16:22 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> This allows you to do "git clone --branch=v1.7.8 git.git" and work
> right away from there. No big deal, just one more convenient step, I
> think. --branch taking a tag may be confusing though.
>
> We can still have master in this case instead of detached HEAD, which
> may make more sense because we use --branch. I don't care much which
> way should be used.

You clone a single lineage of the history, either shallowly or fully,
either starting at the tip of one single branch or a named tag.

What is the expected use scenario of a resulting repository of this new
feature? As this is creating a repository, not a tarball extract, you
certainly would want the user to build further history in the resulting
repository, and it would need a real branch at some point, preferably
before any new commit is made. Which makes me think that the only reason
we would use a detached HEAD would be because we cannot decide what name
to give to that single branch and make it the responsibility of the user
to run "git checkout -b $whatever" as the first thing.

I think the real cause of the above is because this patch and its previous
companion patch conflate the meaning of the "--branch" option with the
purpose of specifying which lineage of the history to copy. The option is
described to name the local branch that is checked out, instead of using
the the same name the remote's primary branch. But these patches abuse the
option to name something different at the same time---the endpoint of the
single lineage to be copied.

These two may often be the same, and use of "clone --branch=master" in
such a case would mean that you want to name the local branch of the final
checkout to be "master" _and_ the endpoint of the single lineage you are
copying is also their "master".

But the "tag" extension proposed with this change is different.

You are specifying an endpoint of the single lineage with the option that
is different from any of the branches at the origin, and because you used
the "--branch" option for that purpose, you lost the way to specify the
primary thing the option wanted to express: what the name of the resulting
checkout should be.

Perhaps something like "clone --branch=master --$endpoint=v1.7.8" that
says "I want a clone of the repository limited to a single lineage, whose
history ends at the commit pointed by the v1.7.8 tag, and name the local
checkout my master branch" be more appropriate?

Also, the user is likely to want to fetch and integrate from the origin
with his own history. How should "git pull" and "git fetch" work in the
resulting repository? What should the remote.origin.* look like?

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH] clone: allow detached checkout when --branch takes a tag
  2012-01-05 16:22 ` Junio C Hamano
@ 2012-01-06  7:35   ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-06  7:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

2012/1/5 Junio C Hamano <gitster@pobox.com>:
> Also, the user is likely to want to fetch and integrate from the origin
> with his own history. How should "git pull" and "git fetch" work in the
> resulting repository? What should the remote.origin.* look like?

fetch should behave the same regardless this patch. remote.origin.*
looks exactly the same. Branch tracking is not set up so pull works
half way.

> You clone a single lineage of the history, either shallowly or fully,
> either starting at the tip of one single branch or a named tag.
>
> What is the expected use scenario of a resulting repository of this new
> feature? As this is creating a repository, not a tarball extract, you
> certainly would want the user to build further history in the resulting
> repository, and it would need a real branch at some point, preferably
> before any new commit is made. Which makes me think that the only reason
> we would use a detached HEAD would be because we cannot decide what name
> to give to that single branch and make it the responsibility of the user
> to run "git checkout -b $whatever" as the first thing.

We can still commit with detached HEAD but I guess it's not
recommended. For a quick, throwaway repository, it's probably OK.

> I think the real cause of the above is because this patch and its previous
> companion patch conflate the meaning of the "--branch" option with the
> purpose of specifying which lineage of the history to copy. The option is
> described to name the local branch that is checked out, instead of using
> the the same name the remote's primary branch. But these patches abuse the
> option to name something different at the same time---the endpoint of the
> single lineage to be copied.
>
> These two may often be the same, and use of "clone --branch=master" in
> such a case would mean that you want to name the local branch of the final
> checkout to be "master" _and_ the endpoint of the single lineage you are
> copying is also their "master".
>
> But the "tag" extension proposed with this change is different.
>
> You are specifying an endpoint of the single lineage with the option that
> is different from any of the branches at the origin, and because you used
> the "--branch" option for that purpose, you lost the way to specify the
> primary thing the option wanted to express: what the name of the resulting
> checkout should be.
>
> Perhaps something like "clone --branch=master --$endpoint=v1.7.8" that
> says "I want a clone of the repository limited to a single lineage, whose
> history ends at the commit pointed by the v1.7.8 tag, and name the local
> checkout my master branch" be more appropriate?

Branch naming is a local thing. Maybe we could get away with always
name it "master" if fetched from a tag. Branch tracking is not set up
in this case, so if they want to follow up with upstream, they have to
do some more themselves (including renaming "master" to something else
for less confusion with upstream "master"). New option is not easily
discovered, while "git branch -r master foo" is quite easy.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH] clone: allow detached checkout when --branch takes a tag
  2012-01-05 14:18 ` Jeff King
@ 2012-01-06 11:09   ` Nguyen Thai Ngoc Duy
  2012-01-06 14:42     ` Jeff King
  0 siblings, 1 reply; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-06 11:09 UTC (permalink / raw)
  To: Jeff King; +Cc: git

2012/1/5 Jeff King <peff@peff.net>:
>> @@ -721,6 +722,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>>                       strbuf_release(&head);
>>
>>                       if (!our_head_points_at) {
>> +                             strbuf_addstr(&head, "refs/tags/");
>> +                             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) {
>
> Hmm. The context just above your patch that got snipped does this:
>
>    strbuf_addstr(&head, src_ref_prefix);
>    strbuf_addstr(&head, option_branch);
>    our_head_points_at =
>        find_ref_by_name(mapped_refs, head.buf);
>
> where src_ref_prefix typically is "refs/heads/", and clearly you are
> meaning to do the same thing for tags. But the use of "src_ref_prefix"
> is interesting.
>
> It is always "refs/heads/" unless we are cloning into a bare mirror, in
> which case it is "refs/". So with your patch in the non-mirror case,
> doing "--branch=foo" would try "refs/heads/foo" followed by
> "refs/tags/foo". Which makes sense. But in the mirror case, it will try
> "refs/foo" followed by "refs/tags/foo", which is kind of odd.
>
> I wonder, though, if the original code makes any sense. By using
> "refs/", I would have to say "--branch=heads/foo", which is kind of
> weird and undocumented. I think it should probably always be
> "refs/heads/", no matter if we are mirroring or not.

--branch should not be used with --mirror in my opinion. --branch
changes HEAD so it's no longer an exact mirror.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH] clone: allow detached checkout when --branch takes a tag
  2012-01-06 11:09   ` Nguyen Thai Ngoc Duy
@ 2012-01-06 14:42     ` Jeff King
  0 siblings, 0 replies; 68+ messages in thread
From: Jeff King @ 2012-01-06 14:42 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git

On Fri, Jan 06, 2012 at 06:09:16PM +0700, Nguyen Thai Ngoc Duy wrote:

> > I wonder, though, if the original code makes any sense. By using
> > "refs/", I would have to say "--branch=heads/foo", which is kind of
> > weird and undocumented. I think it should probably always be
> > "refs/heads/", no matter if we are mirroring or not.
> 
> --branch should not be used with --mirror in my opinion. --branch
> changes HEAD so it's no longer an exact mirror.

You could be making a repo that mirrors all of the refs, but has a
different HEAD (e.g., the upstream has "development" as the main branch,
but you want a local mirror with "production" as the HEAD).

I agree it's an unlikely combination (which is probably why nobody has
complained about the weird behavior), but I don't see a particular
reason to forbid it.

-Peff

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 1/6] t5601: add &&
  2012-01-05 13:49 [PATCH] clone: allow detached checkout when --branch takes a tag Nguyễn Thái Ngọc Duy
  2012-01-05 14:18 ` Jeff King
  2012-01-05 16:22 ` Junio C Hamano
@ 2012-01-08 11:46 ` Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 2/6] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
                     ` (15 more replies)
  2 siblings, 16 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t5601-clone.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 87ee016..49821eb 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -9,9 +9,9 @@ test_expect_success setup '
 	rm -fr .git &&
 	test_create_repo src &&
 	(
-		cd src
-		>file
-		git add file
+		cd src &&
+		>file &&
+		git add file &&
 		git commit -m initial
 	)
 
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 2/6] clone: write detached HEAD in bare repositories
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
@ 2012-01-08 11:46   ` Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 3/6] clone: factor out checkout code Nguyễn Thái Ngọc Duy
                     ` (14 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy

If we don't write, HEAD is still at refs/heads/master as initialized
by init-db, which may or may not match remote's HEAD.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c  |    9 +++------
 t/t5601-clone.sh |   25 ++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 86db954..8dde1ea 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		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;
-		}
+		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)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 49821eb..e0b8db6 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -12,7 +12,10 @@ test_expect_success setup '
 		cd src &&
 		>file &&
 		git add file &&
-		git commit -m initial
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 3/6] clone: factor out checkout code
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 2/6] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
@ 2012-01-08 11:46   ` Nguyễn Thái Ngọc Duy
  2012-01-10  0:32     ` Junio C Hamano
  2012-01-08 11:46   ` [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
                     ` (13 subsequent siblings)
  15 siblings, 1 reply; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy

Read HEAD from disk instead of relying on local variable
our_head_points_at, so that if earlier code fails to make HEAD
properly, it'll be detected.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 8dde1ea..100056d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (strcmp(head, "HEAD")) {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity > 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
 	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		/* Source had detached HEAD pointing somewhere. */
 		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)
-			warning(_("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n"));
-		option_no_checkout = 1;
 	}
 
 	if (transport) {
@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_disconnect(transport);
 	}
 
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = (option_verbosity > 0);
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		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);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die(_("unable to write new index file"));
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(our_head_points_at->old_sha1), "1",
-				NULL);
-
-		if (!err && option_recursive)
-			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
-	}
+	err = checkout();
 
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 2/6] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 3/6] clone: factor out checkout code Nguyễn Thái Ngọc Duy
@ 2012-01-08 11:46   ` Nguyễn Thái Ngọc Duy
  2012-01-10  0:33     ` Junio C Hamano
  2012-01-08 11:46   ` [PATCH v2 5/6] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
                     ` (12 subsequent siblings)
  15 siblings, 1 reply; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy

It does not make sense to look outside refs/heads for HEAD's target
(src_ref_prefix can be set to "refs/" if --mirror is used) because ref
code only allows symref in form refs/heads/...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 100056d..914fd6b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -727,7 +727,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		if (option_branch) {
 			struct strbuf head = STRBUF_INIT;
-			strbuf_addstr(&head, src_ref_prefix);
+			strbuf_addstr(&head, "refs/heads/");
 			strbuf_addstr(&head, option_branch);
 			our_head_points_at =
 				find_ref_by_name(mapped_refs, head.buf);
@@ -763,7 +763,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
+	if (our_head_points_at &&
+	    !prefixcmp(our_head_points_at->name, "refs/heads/")) {
 		/* Local default branch link */
 		create_symref("HEAD", our_head_points_at->name, NULL);
 		if (!option_bare) {
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 5/6] clone: allow --branch to take a tag
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (2 preceding siblings ...)
  2012-01-08 11:46   ` [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-08 11:46   ` Nguyễn Thái Ngọc Duy
  2012-01-08 11:46   ` [PATCH v2 6/6] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
                     ` (11 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy

Because a tag ref cannot be put to HEAD, HEAD will become detached.
This is consistent with "git checkout <tag>".

This is mostly useful in shallow clone, where it allows you to clone a
tag in addtion to branches.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Although 'git clone --depth 1 --branch <tag>' still needs fixups on
 top. I'll do that later.

 Documentation/git-clone.txt |    5 +++--
 builtin/clone.c             |   14 ++++++++++++++
 t/t5601-clone.sh            |    9 +++++++++
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 4b8b26b..db2b29c 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -146,8 +146,9 @@ objects from the source repository into a pack in the cloned repository.
 -b <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
-	instead. In a non-bare repository, this is the branch that will
-	be checked out.
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
diff --git a/builtin/clone.c b/builtin/clone.c
index 914fd6b..0d4b9ab 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -734,6 +734,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			strbuf_release(&head);
 
 			if (!our_head_points_at) {
+				strbuf_addstr(&head, "refs/tags/");
+				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);
@@ -776,6 +784,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			install_branch_config(0, head, option_origin,
 					      our_head_points_at->name);
 		}
+	} else if (our_head_points_at) {
+		const struct ref *ref = our_head_points_at;
+		struct commit *c = lookup_commit_reference(ref->old_sha1);
+		/* Source had detached HEAD pointing somewhere. */
+		update_ref(reflog_msg.buf, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
 		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e0b8db6..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
 	grep /src/\\.git/objects target-10/objects/info/alternates
 '
 
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 6/6] clone: print advice on checking out detached HEAD
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (3 preceding siblings ...)
  2012-01-08 11:46   ` [PATCH v2 5/6] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
@ 2012-01-08 11:46   ` Nguyễn Thái Ngọc Duy
  2012-01-10  0:36     ` Junio C Hamano
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                     ` (10 subsequent siblings)
  15 siblings, 1 reply; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-08 11:46 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c           |   14 ++++++++++++++
 advice.h           |    1 +
 builtin/checkout.c |   16 +---------------
 builtin/clone.c    |    5 ++++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/advice.c b/advice.c
index e02e632..3e1a145 100644
--- a/advice.c
+++ b/advice.c
@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)
 	error_resolve_conflict(me);
 	die("Exiting because of an unresolved conflict.");
 }
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
index e5d0af7..7bda45b 100644
--- a/advice.h
+++ b/advice.h
@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1984d9..5bf96ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)
 	strbuf_release(&sb);
 }
 
-static void detach_advice(const char *old_path, const char *new_name)
-{
-	const char fmt[] =
-	"Note: checking out '%s'.\n\n"
-	"You are in 'detached HEAD' state. You can look around, make experimental\n"
-	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
-	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b new_branch_name\n\n";
-
-	fprintf(stderr, fmt, new_name);
-}
-
 static void update_refs_for_switch(struct checkout_opts *opts,
 				   struct branch_info *old,
 				   struct branch_info *new)
@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
-				detach_advice(old->path, new->name);
+				detach_advice(new->name);
 			describe_detached_head(_("HEAD is now at"), new->commit);
 		}
 	} else if (new->path) {	/* Switch branches. */
diff --git a/builtin/clone.c b/builtin/clone.c
index 0d4b9ab..52596db 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -467,7 +467,10 @@ static int checkout(void)
 			  "unable to checkout.\n"));
 		return 0;
 	}
-	if (strcmp(head, "HEAD")) {
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
 		if (prefixcmp(head, "refs/heads/"))
 			die(_("HEAD not found below refs/heads!"));
 	}
-- 
1.7.8.36.g69ee2

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 3/6] clone: factor out checkout code
  2012-01-08 11:46   ` [PATCH v2 3/6] clone: factor out checkout code Nguyễn Thái Ngọc Duy
@ 2012-01-10  0:32     ` Junio C Hamano
  2012-01-10  2:01       ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-10  0:32 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> Read HEAD from disk instead of relying on local variable
> our_head_points_at, so that if earlier code fails to make HEAD
> properly, it'll be detected.

The end result might be more or less the same with your patch from the
end-user's point of view, but "if earlier code fails", shouldn't you
detect and diagnose it right there?

If you observe lack of "HEAD" in checkout(), you cannot tell if that was
because the remote did not have anything usable in the first place, or
because we knew where it should point at (and may have even attempted to
create it) but somehow failed to make it point at it.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-08 11:46   ` [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-10  0:33     ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2012-01-10  0:33 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Up to this point I find the series makes sense.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD
  2012-01-08 11:46   ` [PATCH v2 6/6] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
@ 2012-01-10  0:36     ` Junio C Hamano
  2012-01-10  1:54       ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-10  0:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

This patch makes 100% sense _if_ we let clone result in a repository with
a detached HEAD, which I am not sure if it is a good idea, or if it is
better to fail the attempt to clone to give incentive to the owner of the
remote repository to fix it.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD
  2012-01-10  0:36     ` Junio C Hamano
@ 2012-01-10  1:54       ` Nguyen Thai Ngoc Duy
  2012-01-10  4:49         ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-10  1:54 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

2012/1/10 Junio C Hamano <gitster@pobox.com>:
> This patch makes 100% sense _if_ we let clone result in a repository with
> a detached HEAD, which I am not sure if it is a good idea, or if it is
> better to fail the attempt to clone to give incentive to the owner of the
> remote repository to fix it.

Then a hostile remote can stop users from cloning his repository by
detaching HEAD? That's not nice. On the other hand, if specifying
--branch=<wrong-branch> leads to detached case, then we should
probably refuse to clone. But that should happen before transferring
the pack.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 3/6] clone: factor out checkout code
  2012-01-10  0:32     ` Junio C Hamano
@ 2012-01-10  2:01       ` Nguyen Thai Ngoc Duy
  2012-01-10  4:59         ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-10  2:01 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

2012/1/10 Junio C Hamano <gitster@pobox.com>:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> Read HEAD from disk instead of relying on local variable
>> our_head_points_at, so that if earlier code fails to make HEAD
>> properly, it'll be detected.
>
> The end result might be more or less the same with your patch from the
> end-user's point of view, but "if earlier code fails", shouldn't you
> detect and diagnose it right there?

Sure, but another fence does not harm. There's also one thing I missed
in the commit message that it makes update head code and checkout code
more independent. Update head code does not need to maintain
our_head_points_at at the end for checkout anymore.

> If you observe lack of "HEAD" in checkout(), you cannot tell if that was
> because the remote did not have anything usable in the first place, or
> because we knew where it should point at (and may have even attempted to
> create it) but somehow failed to make it point at it.

The lack of HEAD probably won't happen because HEAD is created by
default in init-db. This is mainly to catch invalid HEAD (like putting
"refs/tags/something" in HEAD).
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD
  2012-01-10  1:54       ` Nguyen Thai Ngoc Duy
@ 2012-01-10  4:49         ` Junio C Hamano
  2012-01-10  5:54           ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-10  4:49 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Jeff King

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:

> 2012/1/10 Junio C Hamano <gitster@pobox.com>:
>> This patch makes 100% sense _if_ we let clone result in a repository with
>> a detached HEAD, which I am not sure if it is a good idea, or if it is
>> better to fail the attempt to clone to give incentive to the owner of the
>> remote repository to fix it.
>
> Then a hostile remote can stop users from cloning his repository by
> detaching HEAD? That's not nice.

That's crazy talk. Why does anybody from a hostile remote to begin with?

> On the other hand, if specifying --branch=<wrong-branch> leads to
> detached case, then we should probably refuse to clone.

If you mean "nonexistent" by "wrong", yeah, I agree, as we do not know
what the user asked us to pull down in that case.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 3/6] clone: factor out checkout code
  2012-01-10  2:01       ` Nguyen Thai Ngoc Duy
@ 2012-01-10  4:59         ` Junio C Hamano
  2012-01-10  5:57           ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-10  4:59 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Jeff King

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:

> 2012/1/10 Junio C Hamano <gitster@pobox.com>:
>>
>>> Read HEAD from disk instead of relying on local variable
>>> our_head_points_at, so that if earlier code fails to make HEAD
>>> properly, it'll be detected.
>>
>> The end result might be more or less the same with your patch from the
>> end-user's point of view, but "if earlier code fails", shouldn't you
>> detect and diagnose it right there?
>
> Sure, but another fence does not harm.

But that is not "another" fence but is the _only_ fence, as you do not
check after running update_ref of "HEAD".

> There's also one thing I missed in the commit message that it makes
> update head code and checkout code more independent. Update head code
> does not need to maintain our_head_points_at at the end for checkout
> anymore.

I like that reasoning in general. The logic ought to be:

 - Learn what the remote has;

 - Combine it with --branch parameter, determine what local branch our
   head _should_ point at;

 - Make our head point at it, and check it out.

I wonder if we can somehow make the above logic more clear in the
code. Perhaps the first two could be made into a single helper function
"decide_local_branch()", and the third would be the "checkout()" function
in your patch, updated to take "const char *" parameter or something?

> The lack of HEAD probably won't happen because HEAD is created by
> default in init-db. This is mainly to catch invalid HEAD (like putting
> "refs/tags/something" in HEAD).

Sorry; what I meant by "lack" was "... if earlier code fails to make HEAD
properly" case.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 6/6] clone: print advice on checking out detached HEAD
  2012-01-10  4:49         ` Junio C Hamano
@ 2012-01-10  5:54           ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-10  5:54 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

On Tue, Jan 10, 2012 at 11:49 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:
>
>> 2012/1/10 Junio C Hamano <gitster@pobox.com>:
>>> This patch makes 100% sense _if_ we let clone result in a repository with
>>> a detached HEAD, which I am not sure if it is a good idea, or if it is
>>> better to fail the attempt to clone to give incentive to the owner of the
>>> remote repository to fix it.
>>
>> Then a hostile remote can stop users from cloning his repository by
>> detaching HEAD? That's not nice.
>
> That's crazy talk. Why does anybody from a hostile remote to begin with?

The point is, why punish client for remote's problems? If I have to
talk to upstream and wait for them to fix their repository, I might as
well give up cloning and move on. It's OK to annoy users to the point
that they ask upstream for a fix, but we should not disallow clone in
that case.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 3/6] clone: factor out checkout code
  2012-01-10  4:59         ` Junio C Hamano
@ 2012-01-10  5:57           ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-10  5:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

On Tue, Jan 10, 2012 at 11:59 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> There's also one thing I missed in the commit message that it makes
>> update head code and checkout code more independent. Update head code
>> does not need to maintain our_head_points_at at the end for checkout
>> anymore.
>
> I like that reasoning in general. The logic ought to be:
>
>  - Learn what the remote has;
>
>  - Combine it with --branch parameter, determine what local branch our
>   head _should_ point at;
>
>  - Make our head point at it, and check it out.
>
> I wonder if we can somehow make the above logic more clear in the
> code. Perhaps the first two could be made into a single helper function
> "decide_local_branch()", and the third would be the "checkout()" function
> in your patch, updated to take "const char *" parameter or something?

yeah, I split the first two into update_head() but dropped it for some
reasons I don't remember. That would make the main function easier to
follow. I'll look at it again.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 00/10] nd/clone-detached
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (4 preceding siblings ...)
  2012-01-08 11:46   ` [PATCH v2 6/6] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:56   ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                       ` (10 more replies)
  2012-01-10  9:56   ` [PATCH v3 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
                     ` (9 subsequent siblings)
  15 siblings, 11 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:56 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

Compare to v2, this round does more refactoring, which makes
cmd_clone() looks easier to follow in the end, in my opinion.

There's also 7/10 that refuses --branch=<nonexistent>. I don't know if
I react too strong. The current behavior is fall back to remote's HEAD
(and detached HEAD if remote's HEAD is detached). Maybe we should only
refuse it when it leads to detached HEAD and let it fall back to
remote's HEAD otherwise.

The last two patches remain debatable. If we disallow detached HEAD
from new clones, perhaps we could put <tag>^{commit} to
refs/heads/master then drop the last patch. t3501.6, t5527.2, t5707.5,
t7406.29 likes to have detached HEAD, but those can be fixed.

Nguyễn Thái Ngọc Duy (10):
  t5601: add missing && cascade
  clone: write detached HEAD in bare repositories
  clone: factor out checkout code
  clone: factor out HEAD update code
  clone: factor out remote ref writing
  clone: delay cloning until after remote HEAD checking
  clone: --branch=<branch> always means refs/heads/<branch>
  clone: refuse to clone if --branch points to bogus ref
  clone: allow --branch to take a tag
  clone: print advice on checking out detached HEAD

 Documentation/git-clone.txt |    5 +-
 advice.c                    |   14 +++
 advice.h                    |    1 +
 builtin/checkout.c          |   16 +---
 builtin/clone.c             |  252 +++++++++++++++++++++++++------------------
 t/t5601-clone.sh            |   40 ++++++-
 t/t5706-clone-branch.sh     |    8 +-
 transport.c                 |    5 +-
 8 files changed, 207 insertions(+), 134 deletions(-)

-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v3 01/10] t5601: add missing && cascade
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (5 preceding siblings ...)
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:56   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:56   ` [PATCH v3 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
                     ` (8 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:56 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t5601-clone.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 87ee016..49821eb 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -9,9 +9,9 @@ test_expect_success setup '
 	rm -fr .git &&
 	test_create_repo src &&
 	(
-		cd src
-		>file
-		git add file
+		cd src &&
+		>file &&
+		git add file &&
 		git commit -m initial
 	)
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 02/10] clone: write detached HEAD in bare repositories
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (6 preceding siblings ...)
  2012-01-10  9:56   ` [PATCH v3 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:56   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
                     ` (7 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:56 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

If we don't write, HEAD is still at refs/heads/master as initialized
by init-db, which may or may not match remote's HEAD.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c  |    9 +++------
 t/t5601-clone.sh |   25 ++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 86db954..8dde1ea 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		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;
-		}
+		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)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 49821eb..e0b8db6 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -12,7 +12,10 @@ test_expect_success setup '
 		cd src &&
 		>file &&
 		git add file &&
-		git commit -m initial
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 03/10] clone: factor out checkout code
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (7 preceding siblings ...)
  2012-01-10  9:56   ` [PATCH v3 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
                     ` (6 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

Read HEAD from disk instead of relying on local variable
our_head_points_at. This reduces complexity of cmd_clone() a little
bit.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 8dde1ea..100056d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (strcmp(head, "HEAD")) {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity > 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
 	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		/* Source had detached HEAD pointing somewhere. */
 		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)
-			warning(_("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n"));
-		option_no_checkout = 1;
 	}
 
 	if (transport) {
@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_disconnect(transport);
 	}
 
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = (option_verbosity > 0);
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		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);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die(_("unable to write new index file"));
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(our_head_points_at->old_sha1), "1",
-				NULL);
-
-		if (!err && option_recursive)
-			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
-	}
+	err = checkout();
 
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 04/10] clone: factor out HEAD update code
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (8 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
                     ` (5 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   36 +++++++++++++++++++-----------------
 1 files changed, 19 insertions(+), 17 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 100056d..26a037c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,24 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	if (our) {
+		/* Local default branch link */
+		create_symref("HEAD", our->name, NULL);
+		if (!option_bare) {
+			const char *head = skip_prefix(our->name, "refs/heads/");
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (remote) {
+		/* Source had detached HEAD pointing somewhere. */
+		update_ref(msg, "HEAD", remote->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	}
+}
+
 static int checkout(void)
 {
 	unsigned char sha1[20];
@@ -763,23 +781,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", our_head_points_at->name, NULL);
-		if (!option_bare) {
-			const char *head = skip_prefix(our_head_points_at->name,
-						       "refs/heads/");
-			update_ref(reflog_msg.buf, "HEAD",
-				   our_head_points_at->old_sha1,
-				   NULL, 0, DIE_ON_ERR);
-			install_branch_config(0, head, option_origin,
-					      our_head_points_at->name);
-		}
-	} else if (remote_head) {
-		/* Source had detached HEAD pointing somewhere. */
-		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
-	}
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
 	if (transport) {
 		transport_unlock_pack(transport);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 05/10] clone: factor out remote ref writing
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (9 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
                     ` (4 subsequent siblings)
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   36 ++++++++++++++++++++++++------------
 1 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 26a037c..73d07ed 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,28 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg)
+{
+	if (refs) {
+		clear_extra_refs();
+		write_remote_refs(mapped_refs);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      msg);
+	}
+
+}
+
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
@@ -735,10 +757,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	}
 
 	if (refs) {
-		clear_extra_refs();
-
-		write_remote_refs(mapped_refs);
-
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
 			guess_remote_head(remote_head, mapped_refs, 0);
@@ -772,14 +790,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
-	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);
-	}
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (10 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-11 19:36     ` Junio C Hamano
  2012-01-24  0:15     ` Junio C Hamano
  2012-01-10  9:57   ` [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
                     ` (3 subsequent siblings)
  15 siblings, 2 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

This gives us an opportunity to abort the command during remote HEAD
check without wasting much bandwidth.

Cloning with remote-helper remains before the check because the remote
helper updates mapped_refs, which is necessary for remote ref checks.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 I'm not familiar with remote-helper to see if there's any better way
 to do this..

 builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------
 transport.c     |    5 ++++-
 2 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 73d07ed..05224d7 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -361,13 +361,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 	closedir(dir);
 }
 
-static const struct ref *clone_local(const char *src_repo,
-				     const char *dest_repo)
+static void clone_local(const char *src_repo, const char *dest_repo)
 {
-	const struct ref *ret;
-	struct remote *remote;
-	struct transport *transport;
-
 	if (option_shared) {
 		struct strbuf alt = STRBUF_INIT;
 		strbuf_addf(&alt, "%s/objects", src_repo);
@@ -383,13 +378,8 @@ static const struct ref *clone_local(const char *src_repo,
 		strbuf_release(&dest);
 	}
 
-	remote = remote_get(src_repo);
-	transport = transport_get(remote, src_repo);
-	ret = transport_get_remote_refs(transport);
-	transport_disconnect(transport);
 	if (0 <= option_verbosity)
 		printf(_("done.\n"));
-	return ret;
 }
 
 static const char *junk_work_tree;
@@ -576,6 +566,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
 	char *src_ref_prefix = "refs/heads/";
+	struct remote *remote;
 	int err = 0;
 
 	struct refspec *refspec;
@@ -727,13 +718,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	strbuf_reset(&value);
 
-	if (is_local) {
-		refs = clone_local(path, git_dir);
-		mapped_refs = wanted_peer_refs(refs, refspec);
-	} else {
-		struct remote *remote = remote_get(option_origin);
-		transport = transport_get(remote, remote->url[0]);
+	remote = remote_get(option_origin);
+	transport = transport_get(remote, remote->url[0]);
 
+	if (!is_local) {
 		if (!transport->get_refs_list || !transport->fetch)
 			die(_("Don't know how to clone %s"), transport->url);
 
@@ -748,14 +736,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
-
-		refs = transport_get_remote_refs(transport);
-		if (refs) {
-			mapped_refs = wanted_peer_refs(refs, refspec);
-			transport_fetch_refs(transport, mapped_refs);
-		}
 	}
 
+	refs = transport_get_remote_refs(transport);
+	mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
+
+	/*
+	 * mapped_refs may be updated if transport-helper is used so
+	 * we need fetch it early because remote_head code below
+	 * relies on it.
+	 *
+	 * for normal clones, transport_get_remote_refs() should
+	 * return reliable ref set, we can delay cloning until after
+	 * remote HEAD check.
+	 */
+	if (!is_local && remote->foreign_vcs && refs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	if (refs) {
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
@@ -790,15 +787,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && !remote->foreign_vcs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
 			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-	if (transport) {
-		transport_unlock_pack(transport);
-		transport_disconnect(transport);
-	}
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
 
 	err = checkout();
 
diff --git a/transport.c b/transport.c
index a99b7c9..aae9889 100644
--- a/transport.c
+++ b/transport.c
@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		while (is_urlschemechar(p == url, *p))
 			p++;
-		if (!prefixcmp(p, "::"))
+		if (!prefixcmp(p, "::")) {
 			helper = xstrndup(url, p - url);
+			remote->foreign_vcs = helper;
+		}
 	}
 
 	if (helper) {
@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		char *handler = xmalloc(len + 1);
 		handler[len] = 0;
 		strncpy(handler, url, len);
+		remote->foreign_vcs = helper;
 		transport_helper_init(ret, handler);
 	}
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (11 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-11 20:01     ` Junio C Hamano
  2012-01-10  9:57   ` [PATCH v3 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
                     ` (2 subsequent siblings)
  15 siblings, 1 reply; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

It does not make sense to look outside refs/heads for HEAD's target
(src_ref_prefix can be set to "refs/" if --mirror is used) because ref
code only allows symref in form refs/heads/...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 05224d7..960242f 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
-	if (our) {
+	if (our && !prefixcmp(our->name, "refs/heads/")) {
 		/* Local default branch link */
 		create_symref("HEAD", our->name, NULL);
 		if (!option_bare) {
@@ -760,7 +760,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		if (option_branch) {
 			struct strbuf head = STRBUF_INIT;
-			strbuf_addstr(&head, src_ref_prefix);
+			strbuf_addstr(&head, "refs/heads/");
 			strbuf_addstr(&head, option_branch);
 			our_head_points_at =
 				find_ref_by_name(mapped_refs, head.buf);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 08/10] clone: refuse to clone if --branch points to bogus ref
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (12 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
  2012-01-10  9:57   ` [PATCH v3 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

It's possible that users make a typo in the branch name. Stop and let
users recheck. Falling back to remote's HEAD is not documented any
way.

Except when using remote helper, the pack has not been transferred at
this stage yet so we don't waste much bandwidth.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c         |   10 ++++------
 t/t5706-clone-branch.sh |    8 ++------
 2 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 960242f..f751997 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -766,12 +766,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 				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;
-			}
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in "
+				      "upstream %s, using HEAD instead"),
+				    option_branch, option_origin);
 		}
 		else
 			our_head_points_at = remote_head_points_at;
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
index f3f9a76..56be67e 100755
--- a/t/t5706-clone-branch.sh
+++ b/t/t5706-clone-branch.sh
@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
 	)
 '
 
-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_expect_success 'clone -b with bogus branch' '
+	test_must_fail git clone -b bogus parent clone-bogus
 '
 
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 09/10] clone: allow --branch to take a tag
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (13 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  2012-01-11 23:57     ` Junio C Hamano
  2012-01-10  9:57   ` [PATCH v3 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  15 siblings, 1 reply; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

Because a tag ref cannot be put to HEAD, HEAD will become detached.
This is consistent with "git checkout <tag>".

This is mostly useful in shallow clone, where it allows you to clone a
tag in addtion to branches.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clone.txt |    5 +++--
 builtin/clone.c             |   13 +++++++++++++
 t/t5601-clone.sh            |    9 +++++++++
 3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 4b8b26b..db2b29c 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -146,8 +146,9 @@ objects from the source repository into a pack in the cloned repository.
 -b <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
-	instead. In a non-bare repository, this is the branch that will
-	be checked out.
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
diff --git a/builtin/clone.c b/builtin/clone.c
index f751997..ed18c1a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -471,6 +471,11 @@ static void update_head(const struct ref *our, const struct ref *remote,
 			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
 			install_branch_config(0, head, option_origin, our->name);
 		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(our->old_sha1);
+		/* Source had detached HEAD pointing somewhere. */
+		update_ref(msg, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
 	} else if (remote) {
 		/* Source had detached HEAD pointing somewhere. */
 		update_ref(msg, "HEAD", remote->old_sha1,
@@ -766,6 +771,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 				find_ref_by_name(mapped_refs, head.buf);
 			strbuf_release(&head);
 
+			if (!our_head_points_at) {
+				strbuf_addstr(&head, "refs/tags/");
+				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)
 				die(_("Remote branch %s not found in "
 				      "upstream %s, using HEAD instead"),
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e0b8db6..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
 	grep /src/\\.git/objects target-10/objects/info/alternates
 '
 
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v3 10/10] clone: print advice on checking out detached HEAD
  2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
                     ` (14 preceding siblings ...)
  2012-01-10  9:57   ` [PATCH v3 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
@ 2012-01-10  9:57   ` Nguyễn Thái Ngọc Duy
  15 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-10  9:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c           |   14 ++++++++++++++
 advice.h           |    1 +
 builtin/checkout.c |   16 +---------------
 builtin/clone.c    |    5 ++++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/advice.c b/advice.c
index e02e632..3e1a145 100644
--- a/advice.c
+++ b/advice.c
@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)
 	error_resolve_conflict(me);
 	die("Exiting because of an unresolved conflict.");
 }
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
index e5d0af7..7bda45b 100644
--- a/advice.h
+++ b/advice.h
@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1984d9..5bf96ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)
 	strbuf_release(&sb);
 }
 
-static void detach_advice(const char *old_path, const char *new_name)
-{
-	const char fmt[] =
-	"Note: checking out '%s'.\n\n"
-	"You are in 'detached HEAD' state. You can look around, make experimental\n"
-	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
-	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b new_branch_name\n\n";
-
-	fprintf(stderr, fmt, new_name);
-}
-
 static void update_refs_for_switch(struct checkout_opts *opts,
 				   struct branch_info *old,
 				   struct branch_info *new)
@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
-				detach_advice(old->path, new->name);
+				detach_advice(new->name);
 			describe_detached_head(_("HEAD is now at"), new->commit);
 		}
 	} else if (new->path) {	/* Switch branches. */
diff --git a/builtin/clone.c b/builtin/clone.c
index ed18c1a..70f0280 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -502,7 +502,10 @@ static int checkout(void)
 			  "unable to checkout.\n"));
 		return 0;
 	}
-	if (strcmp(head, "HEAD")) {
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
 		if (prefixcmp(head, "refs/heads/"))
 			die(_("HEAD not found below refs/heads!"));
 	}
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-10  9:57   ` [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
@ 2012-01-11 19:36     ` Junio C Hamano
  2012-01-12  1:27       ` Nguyen Thai Ngoc Duy
  2012-01-24  0:15     ` Junio C Hamano
  1 sibling, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-11 19:36 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> This gives us an opportunity to abort the command during remote HEAD
> check without wasting much bandwidth.
>
> Cloning with remote-helper remains before the check because the remote
> helper updates mapped_refs, which is necessary for remote ref checks.
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  I'm not familiar with remote-helper to see if there's any better way
>  to do this..
> ...
> +	refs = transport_get_remote_refs(transport);
> +	mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
> +
> +	/*
> +	 * mapped_refs may be updated if transport-helper is used so
> +	 * we need fetch it early because remote_head code below
> +	 * relies on it.
> +	 *
> +	 * for normal clones, transport_get_remote_refs() should
> +	 * return reliable ref set, we can delay cloning until after
> +	 * remote HEAD check.
> +	 */
> +	if (!is_local && remote->foreign_vcs && refs)
> +		transport_fetch_refs(transport, mapped_refs);
> +

I like the goal of this change, but wouldn't remote->url indicate it is a
remote that needs a helper invocation by having double-colon in it,
without having an explicit value in foreign_vcs field?

Would it be cleaner if transport_get() learned to mark the transport as
needing special treatment (i.e. we won't know the final ref mapping until
we fetch the data from the other side) and check is made on that mark left
in the transport, instead of foreign_vcs in remote?

> diff --git a/transport.c b/transport.c
> index a99b7c9..aae9889 100644
> --- a/transport.c
> +++ b/transport.c
> @@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
>  
>  		while (is_urlschemechar(p == url, *p))
>  			p++;
> -		if (!prefixcmp(p, "::"))
> +		if (!prefixcmp(p, "::")) {
>  			helper = xstrndup(url, p - url);
> +			remote->foreign_vcs = helper;
> +		}
>  	}
>  
>  	if (helper) {

Ahhh, Ok. You are reusing the existing "foreign_vcs" field exactly for
that purpose. Earlier the field was strictly for configured .vcs value,
but now you use it also for the helper embedded within the URL, which
sounds like the right change to me.

> @@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
>  		char *handler = xmalloc(len + 1);
>  		handler[len] = 0;
>  		strncpy(handler, url, len);
> +		remote->foreign_vcs = helper;
>  		transport_helper_init(ret, handler);
>  	}

This I am not sure. What value does "helper" variable have at this point
in the flow? Wouldn't it be a NULL? Or did you mean "handler"?

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-10  9:57   ` [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-11 20:01     ` Junio C Hamano
  2012-01-12  3:00       ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-11 20:01 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> It does not make sense to look outside refs/heads for HEAD's target
> (src_ref_prefix can be set to "refs/" if --mirror is used) because ref
> code only allows symref in form refs/heads/...
>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/clone.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/clone.c b/builtin/clone.c
> index 05224d7..960242f 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,
>  static void update_head(const struct ref *our, const struct ref *remote,
>  			const char *msg)
>  {
> -	if (our) {
> +	if (our && !prefixcmp(our->name, "refs/heads/")) {
>  		/* Local default branch link */
>  		create_symref("HEAD", our->name, NULL);
>  		if (!option_bare) {

I think this makes sense. In the following three casees:

 - When cloning without --branch, if we could not find a branch that match
   HEAD at the remote;

 - When cloning with --branch, if we did not see such a branch at the
   remote; or

 - When cloning from an empty repository

we come here with "our" set to NULL. Additionally, if the remote HEAD
points outside refs/heads/ and the transport could detect that case
(e.g. a helper that reads from ls-remote output), we can see our->name
outside refs/heads/. Is there any other case where our is not NULL and
our->name does not start with refs/heads/?

The "else if (remote)" clause (not shown, outside the context) that
follows still has comments that says it is a case for "Source had detached
HEAD pointing somewhere", and needs to be adjusted for this change, no? It
is "(1) we know the HEAD points at a non-branch, (2) HEAD may point at a
branch but we do not know which one, or (3) we asked for a specific branch
but it did not exist there" (cloning from an empty will have NULL in
remote and the comment would not apply to that case).

> @@ -760,7 +760,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  
>  		if (option_branch) {
>  			struct strbuf head = STRBUF_INIT;
> -			strbuf_addstr(&head, src_ref_prefix);
> +			strbuf_addstr(&head, "refs/heads/");
>  			strbuf_addstr(&head, option_branch);
>  			our_head_points_at =
>  				find_ref_by_name(mapped_refs, head.buf);

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 09/10] clone: allow --branch to take a tag
  2012-01-10  9:57   ` [PATCH v3 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
@ 2012-01-11 23:57     ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2012-01-11 23:57 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> @@ -766,6 +771,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  				find_ref_by_name(mapped_refs, head.buf);
>  			strbuf_release(&head);
>  
> +			if (!our_head_points_at) {
> +				strbuf_addstr(&head, "refs/tags/");
> +				strbuf_addstr(&head, option_branch);
> +				our_head_points_at =
> +					find_ref_by_name(mapped_refs, head.buf);
> +				strbuf_release(&head);
> +			}
> +

Ok. I think this makes sense.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-11 19:36     ` Junio C Hamano
@ 2012-01-12  1:27       ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-12  1:27 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

2012/1/12 Junio C Hamano <gitster@pobox.com>:
>> @@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
>>               char *handler = xmalloc(len + 1);
>>               handler[len] = 0;
>>               strncpy(handler, url, len);
>> +             remote->foreign_vcs = helper;
>>               transport_helper_init(ret, handler);
>>       }
>
> This I am not sure. What value does "helper" variable have at this point
> in the flow? Wouldn't it be a NULL? Or did you mean "handler"?

Ah yes "handler", my bad.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-11 20:01     ` Junio C Hamano
@ 2012-01-12  3:00       ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-12  3:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

2012/1/12 Junio C Hamano <gitster@pobox.com>:
> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
>
>> It does not make sense to look outside refs/heads for HEAD's target
>> (src_ref_prefix can be set to "refs/" if --mirror is used) because ref
>> code only allows symref in form refs/heads/...
>>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>>  builtin/clone.c |    4 ++--
>>  1 files changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/builtin/clone.c b/builtin/clone.c
>> index 05224d7..960242f 100644
>> --- a/builtin/clone.c
>> +++ b/builtin/clone.c
>> @@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,
>>  static void update_head(const struct ref *our, const struct ref *remote,
>>                       const char *msg)
>>  {
>> -     if (our) {
>> +     if (our && !prefixcmp(our->name, "refs/heads/")) {
>>               /* Local default branch link */
>>               create_symref("HEAD", our->name, NULL);
>>               if (!option_bare) {
>
> I think this makes sense. In the following three casees:
>
>  - When cloning without --branch, if we could not find a branch that match
>   HEAD at the remote;
>
>  - When cloning with --branch, if we did not see such a branch at the
>   remote; or
>
>  - When cloning from an empty repository
>
> we come here with "our" set to NULL. Additionally, if the remote HEAD
> points outside refs/heads/ and the transport could detect that case
> (e.g. a helper that reads from ls-remote output), we can see our->name
> outside refs/heads/. Is there any other case where our is not NULL and
> our->name does not start with refs/heads/?

No, this patch pretty much guarantees that our->name must be inside
refs/heads, unless remote's HEAD points outside, which makes it a
broken remote and git-upload-pack should refuse to serve in the first
place. Until --branch=tag comes into the picture.

> The "else if (remote)" clause (not shown, outside the context) that
> follows still has comments that says it is a case for "Source had detached
> HEAD pointing somewhere", and needs to be adjusted for this change, no? It
> is "(1) we know the HEAD points at a non-branch, (2) HEAD may point at a
> branch but we do not know which one, or (3) we asked for a specific branch
> but it did not exist there" (cloning from an empty will have NULL in
> remote and the comment would not apply to that case).

Yes.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 00/10] nd/clone-detached
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13 19:52       ` Junio C Hamano
                         ` (11 more replies)
  2012-01-13  7:21     ` [PATCH v4 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
                       ` (9 subsequent siblings)
  10 siblings, 12 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Some comment updates after discussion and squash in the fixup patch.
The code is exactly the same as nd/clone-detached in pu.

Nguyễn Thái Ngọc Duy (10):
  t5601: add missing && cascade
  clone: write detached HEAD in bare repositories
  clone: factor out checkout code
  clone: factor out HEAD update code
  clone: factor out remote ref writing
  clone: delay cloning until after remote HEAD checking
  clone: --branch=<branch> always means refs/heads/<branch>
  clone: refuse to clone if --branch points to bogus ref
  clone: allow --branch to take a tag
  clone: print advice on checking out detached HEAD

 Documentation/git-clone.txt |    5 +-
 advice.c                    |   14 +++
 advice.h                    |    1 +
 builtin/checkout.c          |   16 +---
 builtin/clone.c             |  256 +++++++++++++++++++++++++------------------
 t/t5601-clone.sh            |   40 ++++++-
 t/t5706-clone-branch.sh     |    8 +-
 transport.c                 |    5 +-
 8 files changed, 211 insertions(+), 134 deletions(-)

-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v4 01/10] t5601: add missing && cascade
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
                       ` (8 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t5601-clone.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 87ee016..49821eb 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -9,9 +9,9 @@ test_expect_success setup '
 	rm -fr .git &&
 	test_create_repo src &&
 	(
-		cd src
-		>file
-		git add file
+		cd src &&
+		>file &&
+		git add file &&
 		git commit -m initial
 	)
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 02/10] clone: write detached HEAD in bare repositories
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
                       ` (7 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

If we don't write, HEAD is still at refs/heads/master as initialized
by init-db, which may or may not match remote's HEAD.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c  |    9 +++------
 t/t5601-clone.sh |   25 ++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 86db954..8dde1ea 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -720,12 +720,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		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;
-		}
+		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)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 49821eb..e0b8db6 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -12,7 +12,10 @@ test_expect_success setup '
 		cd src &&
 		>file &&
 		git add file &&
-		git commit -m initial
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 03/10] clone: factor out checkout code
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (2 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
                       ` (6 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Read HEAD from disk instead of relying on local variable
our_head_points_at, so that if earlier code fails to make HEAD
properly, it'll be detected.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 8dde1ea..100056d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,63 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (strcmp(head, "HEAD")) {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity > 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
 	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
@@ -722,13 +779,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		/* Source had detached HEAD pointing somewhere. */
 		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)
-			warning(_("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n"));
-		option_no_checkout = 1;
 	}
 
 	if (transport) {
@@ -736,42 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_disconnect(transport);
 	}
 
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = (option_verbosity > 0);
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		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);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die(_("unable to write new index file"));
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(our_head_points_at->old_sha1), "1",
-				NULL);
-
-		if (!err && option_recursive)
-			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
-	}
+	err = checkout();
 
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 04/10] clone: factor out HEAD update code
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (3 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
                       ` (5 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

While at it, update the comment at "if (remote_head)"

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   41 ++++++++++++++++++++++++-----------------
 1 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 100056d..baed5ae 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,29 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	if (our) {
+		/* Local default branch link */
+		create_symref("HEAD", our->name, NULL);
+		if (!option_bare) {
+			const char *head = skip_prefix(our->name, "refs/heads/");
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (remote) {
+		/*
+		 * We know remote HEAD points to a non-branch, or
+		 * HEAD points to a branch but we don't know which one, or
+		 * we asked for a specific branch but it did not exist.
+		 * Detach HEAD in all these cases.
+		 */
+		update_ref(msg, "HEAD", remote->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	}
+}
+
 static int checkout(void)
 {
 	unsigned char sha1[20];
@@ -763,23 +786,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", our_head_points_at->name, NULL);
-		if (!option_bare) {
-			const char *head = skip_prefix(our_head_points_at->name,
-						       "refs/heads/");
-			update_ref(reflog_msg.buf, "HEAD",
-				   our_head_points_at->old_sha1,
-				   NULL, 0, DIE_ON_ERR);
-			install_branch_config(0, head, option_origin,
-					      our_head_points_at->name);
-		}
-	} else if (remote_head) {
-		/* Source had detached HEAD pointing somewhere. */
-		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
-	}
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
 	if (transport) {
 		transport_unlock_pack(transport);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 05/10] clone: factor out remote ref writing
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (4 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
                       ` (4 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   36 ++++++++++++++++++++++++------------
 1 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index baed5ae..d1d6dca 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -448,6 +448,28 @@ static void write_remote_refs(const struct ref *local_refs)
 	clear_extra_refs();
 }
 
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg)
+{
+	if (refs) {
+		clear_extra_refs();
+		write_remote_refs(mapped_refs);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      msg);
+	}
+
+}
+
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
@@ -740,10 +762,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	}
 
 	if (refs) {
-		clear_extra_refs();
-
-		write_remote_refs(mapped_refs);
-
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
 			guess_remote_head(remote_head, mapped_refs, 0);
@@ -777,14 +795,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
-	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);
-	}
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (5 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:21     ` [PATCH v4 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
                       ` (3 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

This gives us an opportunity to abort the command during remote HEAD
check without wasting much bandwidth.

Cloning with remote-helper remains before the check because the remote
helper updates mapped_refs, which is necessary for remote ref checks.
foreign_vcs field is used to indicate the transport is handled by
remote helper.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------
 transport.c     |    5 ++++-
 2 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index d1d6dca..a9d06a4 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -361,13 +361,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 	closedir(dir);
 }
 
-static const struct ref *clone_local(const char *src_repo,
-				     const char *dest_repo)
+static void clone_local(const char *src_repo, const char *dest_repo)
 {
-	const struct ref *ret;
-	struct remote *remote;
-	struct transport *transport;
-
 	if (option_shared) {
 		struct strbuf alt = STRBUF_INIT;
 		strbuf_addf(&alt, "%s/objects", src_repo);
@@ -383,13 +378,8 @@ static const struct ref *clone_local(const char *src_repo,
 		strbuf_release(&dest);
 	}
 
-	remote = remote_get(src_repo);
-	transport = transport_get(remote, src_repo);
-	ret = transport_get_remote_refs(transport);
-	transport_disconnect(transport);
 	if (0 <= option_verbosity)
 		printf(_("done.\n"));
-	return ret;
 }
 
 static const char *junk_work_tree;
@@ -581,6 +571,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
 	char *src_ref_prefix = "refs/heads/";
+	struct remote *remote;
 	int err = 0;
 
 	struct refspec *refspec;
@@ -732,13 +723,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	strbuf_reset(&value);
 
-	if (is_local) {
-		refs = clone_local(path, git_dir);
-		mapped_refs = wanted_peer_refs(refs, refspec);
-	} else {
-		struct remote *remote = remote_get(option_origin);
-		transport = transport_get(remote, remote->url[0]);
+	remote = remote_get(option_origin);
+	transport = transport_get(remote, remote->url[0]);
 
+	if (!is_local) {
 		if (!transport->get_refs_list || !transport->fetch)
 			die(_("Don't know how to clone %s"), transport->url);
 
@@ -753,14 +741,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
-
-		refs = transport_get_remote_refs(transport);
-		if (refs) {
-			mapped_refs = wanted_peer_refs(refs, refspec);
-			transport_fetch_refs(transport, mapped_refs);
-		}
 	}
 
+	refs = transport_get_remote_refs(transport);
+	mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
+
+	/*
+	 * mapped_refs may be updated if transport-helper is used so
+	 * we need fetch it early because remote_head code below
+	 * relies on it.
+	 *
+	 * for normal clones, transport_get_remote_refs() should
+	 * return reliable ref set, we can delay cloning until after
+	 * remote HEAD check.
+	 */
+	if (!is_local && remote->foreign_vcs && refs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	if (refs) {
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
@@ -795,15 +792,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && !remote->foreign_vcs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
 			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-	if (transport) {
-		transport_unlock_pack(transport);
-		transport_disconnect(transport);
-	}
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
 
 	err = checkout();
 
diff --git a/transport.c b/transport.c
index a99b7c9..4366639 100644
--- a/transport.c
+++ b/transport.c
@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		while (is_urlschemechar(p == url, *p))
 			p++;
-		if (!prefixcmp(p, "::"))
+		if (!prefixcmp(p, "::")) {
 			helper = xstrndup(url, p - url);
+			remote->foreign_vcs = helper;
+		}
 	}
 
 	if (helper) {
@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		char *handler = xmalloc(len + 1);
 		handler[len] = 0;
 		strncpy(handler, url, len);
+		remote->foreign_vcs = handler;
 		transport_helper_init(ret, handler);
 	}
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 07/10] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (6 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:21     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:22     ` [PATCH v4 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
                       ` (2 subsequent siblings)
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:21 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

It does not make sense to look outside refs/heads for HEAD's target
(src_ref_prefix can be set to "refs/" if --mirror is used) because ref
code only allows symref in form refs/heads/...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index a9d06a4..89ba6f0 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -463,7 +463,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
-	if (our) {
+	if (our && !prefixcmp(our->name, "refs/heads/")) {
 		/* Local default branch link */
 		create_symref("HEAD", our->name, NULL);
 		if (!option_bare) {
@@ -765,7 +765,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 		if (option_branch) {
 			struct strbuf head = STRBUF_INIT;
-			strbuf_addstr(&head, src_ref_prefix);
+			strbuf_addstr(&head, "refs/heads/");
 			strbuf_addstr(&head, option_branch);
 			our_head_points_at =
 				find_ref_by_name(mapped_refs, head.buf);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 08/10] clone: refuse to clone if --branch points to bogus ref
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (7 preceding siblings ...)
  2012-01-13  7:21     ` [PATCH v4 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:22     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:22     ` [PATCH v4 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
  2012-01-13  7:22     ` [PATCH v4 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

It's possible that users make a typo in the branch name. Stop and let
users recheck. Falling back to remote's HEAD is not documented any
way.

Except when using remote helper, the pack has not been transferred at
this stage yet so we don't waste much bandwidth.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c         |   13 +++++--------
 t/t5706-clone-branch.sh |    8 ++------
 2 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 89ba6f0..6a2886a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -474,8 +474,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
-		 * HEAD points to a branch but we don't know which one, or
-		 * we asked for a specific branch but it did not exist.
+		 * HEAD points to a branch but we don't know which one.
 		 * Detach HEAD in all these cases.
 		 */
 		update_ref(msg, "HEAD", remote->old_sha1,
@@ -771,12 +770,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 				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;
-			}
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in "
+				      "upstream %s, using HEAD instead"),
+				    option_branch, option_origin);
 		}
 		else
 			our_head_points_at = remote_head_points_at;
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
index f3f9a76..56be67e 100755
--- a/t/t5706-clone-branch.sh
+++ b/t/t5706-clone-branch.sh
@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
 	)
 '
 
-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_expect_success 'clone -b with bogus branch' '
+	test_must_fail git clone -b bogus parent clone-bogus
 '
 
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 09/10] clone: allow --branch to take a tag
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (8 preceding siblings ...)
  2012-01-13  7:22     ` [PATCH v4 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:22     ` Nguyễn Thái Ngọc Duy
  2012-01-13  7:22     ` [PATCH v4 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Because a tag ref cannot be put to HEAD, HEAD will become detached.
This is consistent with "git checkout <tag>".

This is mostly useful in shallow clone, where it allows you to clone a
tag in addtion to branches.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clone.txt |    5 +++--
 builtin/clone.c             |   13 +++++++++++++
 t/t5601-clone.sh            |    9 +++++++++
 3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 4b8b26b..db2b29c 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -146,8 +146,9 @@ objects from the source repository into a pack in the cloned repository.
 -b <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
-	instead. In a non-bare repository, this is the branch that will
-	be checked out.
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
diff --git a/builtin/clone.c b/builtin/clone.c
index 6a2886a..a3c8f78 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -471,6 +471,11 @@ static void update_head(const struct ref *our, const struct ref *remote,
 			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
 			install_branch_config(0, head, option_origin, our->name);
 		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(our->old_sha1);
+		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
+		update_ref(msg, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
@@ -770,6 +775,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 				find_ref_by_name(mapped_refs, head.buf);
 			strbuf_release(&head);
 
+			if (!our_head_points_at) {
+				strbuf_addstr(&head, "refs/tags/");
+				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)
 				die(_("Remote branch %s not found in "
 				      "upstream %s, using HEAD instead"),
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e0b8db6..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
 	grep /src/\\.git/objects target-10/objects/info/alternates
 '
 
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v4 10/10] clone: print advice on checking out detached HEAD
  2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
                       ` (9 preceding siblings ...)
  2012-01-13  7:22     ` [PATCH v4 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
@ 2012-01-13  7:22     ` Nguyễn Thái Ngọc Duy
  10 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-13  7:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c           |   14 ++++++++++++++
 advice.h           |    1 +
 builtin/checkout.c |   16 +---------------
 builtin/clone.c    |    5 ++++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/advice.c b/advice.c
index e02e632..3e1a145 100644
--- a/advice.c
+++ b/advice.c
@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)
 	error_resolve_conflict(me);
 	die("Exiting because of an unresolved conflict.");
 }
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
index e5d0af7..7bda45b 100644
--- a/advice.h
+++ b/advice.h
@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1984d9..5bf96ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)
 	strbuf_release(&sb);
 }
 
-static void detach_advice(const char *old_path, const char *new_name)
-{
-	const char fmt[] =
-	"Note: checking out '%s'.\n\n"
-	"You are in 'detached HEAD' state. You can look around, make experimental\n"
-	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
-	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b new_branch_name\n\n";
-
-	fprintf(stderr, fmt, new_name);
-}
-
 static void update_refs_for_switch(struct checkout_opts *opts,
 				   struct branch_info *old,
 				   struct branch_info *new)
@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
-				detach_advice(old->path, new->name);
+				detach_advice(new->name);
 			describe_detached_head(_("HEAD is now at"), new->commit);
 		}
 	} else if (new->path) {	/* Switch branches. */
diff --git a/builtin/clone.c b/builtin/clone.c
index a3c8f78..bfdafaa 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -506,7 +506,10 @@ static int checkout(void)
 			  "unable to checkout.\n"));
 		return 0;
 	}
-	if (strcmp(head, "HEAD")) {
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
 		if (prefixcmp(head, "refs/heads/"))
 			die(_("HEAD not found below refs/heads!"));
 	}
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v4 00/10] nd/clone-detached
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
@ 2012-01-13 19:52       ` Junio C Hamano
  2012-01-14  4:48         ` Nguyen Thai Ngoc Duy
  2012-01-16  9:46       ` [PATCH v5 00/10] nd/clone-detached rebase onto nd/clone-single-branch Nguyễn Thái Ngọc Duy
                         ` (10 subsequent siblings)
  11 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-13 19:52 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Thanks, replaced (and updated comment strings read much better).

There were some conlicts I had to resolve while merging this to 'pu'.
I would appreciate it if you can eyeball it to make sure I didn't make
silly mistakes there.

Thanks.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v4 00/10] nd/clone-detached
  2012-01-13 19:52       ` Junio C Hamano
@ 2012-01-14  4:48         ` Nguyen Thai Ngoc Duy
  2012-01-14  6:53           ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-14  4:48 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

2012/1/14 Junio C Hamano <gitster@pobox.com>:
> Thanks, replaced (and updated comment strings read much better).
>
> There were some conlicts I had to resolve while merging this to 'pu'.
> I would appreciate it if you can eyeball it to make sure I didn't make
> silly mistakes there.

Right, the conflict with nd/clone-single-branch. I kept thinking there
would not be conflict because clone-single-branch's big change was in
wanted_peer_refs() and missed write_followtags() call. The merge looks
good.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v4 00/10] nd/clone-detached
  2012-01-14  4:48         ` Nguyen Thai Ngoc Duy
@ 2012-01-14  6:53           ` Junio C Hamano
  2012-01-14  7:40             ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-14  6:53 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:

> 2012/1/14 Junio C Hamano <gitster@pobox.com>:
>> Thanks, replaced (and updated comment strings read much better).
>>
>> There were some conlicts I had to resolve while merging this to 'pu'.
>> I would appreciate it if you can eyeball it to make sure I didn't make
>> silly mistakes there.
>
> Right, the conflict with nd/clone-single-branch. I kept thinking there
> would not be conflict because clone-single-branch's big change was in
> wanted_peer_refs() and missed write_followtags() call. The merge looks
> good.

Hmm, 'pu' seems to fail its selftest with this merge present, though.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v4 00/10] nd/clone-detached
  2012-01-14  6:53           ` Junio C Hamano
@ 2012-01-14  7:40             ` Nguyen Thai Ngoc Duy
  2012-01-15  2:34               ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-14  7:40 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Fri, Jan 13, 2012 at 10:53:11PM -0800, Junio C Hamano wrote:
> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:
> 
> > 2012/1/14 Junio C Hamano <gitster@pobox.com>:
> >> Thanks, replaced (and updated comment strings read much better).
> >>
> >> There were some conlicts I had to resolve while merging this to 'pu'.
> >> I would appreciate it if you can eyeball it to make sure I didn't make
> >> silly mistakes there.
> >
> > Right, the conflict with nd/clone-single-branch. I kept thinking there
> > would not be conflict because clone-single-branch's big change was in
> > wanted_peer_refs() and missed write_followtags() call. The merge looks
> > good.
> 
> Hmm, 'pu' seems to fail its selftest with this merge present, though.

The commit "refuse to clone if --branch points to bogus ref" from this
series changes clone's behavior that t5500.31, which is added in
nd/clone-single-branch, relies on. This makes "make test" pass for me
on pu:

-- 8< --
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 7e85c71..c4e675f 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -283,10 +283,7 @@ test_expect_success 'clone shallow object count' '
 '
 
 test_expect_success 'clone shallow with nonexistent --branch' '
-	git clone --depth 1 --branch Z "file://$(pwd)/." shallow4 &&
-	GIT_DIR=shallow4/.git git rev-parse HEAD >actual &&
-	git rev-parse HEAD >expected &&
-	test_cmp expected actual
+	test_must_fail git clone --depth 1 --branch Z "file://$(pwd)/." shallow4
 '
 
 test_expect_success 'clone shallow with detached HEAD' '
-- 8< --

I'd rather remove the test, but removing something in a merge does not
sound wise.

Another cleaner approach is to combine the two clone series into
one. If you want to go this way, pick one as base, I'll rebase the
other on top and resend.
-- 
Duy

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v4 00/10] nd/clone-detached
  2012-01-14  7:40             ` Nguyen Thai Ngoc Duy
@ 2012-01-15  2:34               ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2012-01-15  2:34 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:

>> Hmm, 'pu' seems to fail its selftest with this merge present, though.
>
> The commit "refuse to clone if --branch points to bogus ref" from this
> series changes clone's behavior that t5500.31, which is added in
> nd/clone-single-branch, relies on.

Ahh, of course.

> Another cleaner approach is to combine the two clone series into
> one.

I think "--single-branch" is much more relevant compared to cloning from a
repository with some funkiness like detached HEAD which is more about a
theoretical fun exercise, so let's rebuild this on top of the other one.

Thanks.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 00/10] nd/clone-detached rebase onto nd/clone-single-branch
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
  2012-01-13 19:52       ` Junio C Hamano
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
                         ` (9 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

There are a few more changes than just a simple rebase so that
"git clone --depth=1 --branch=tag" also works.

Nguyễn Thái Ngọc Duy (10):
  t5601: add missing && cascade
  clone: write detached HEAD in bare repositories
  clone: factor out checkout code
  clone: factor out HEAD update code
  clone: factor out remote ref writing
  clone: delay cloning until after remote HEAD checking
  clone: --branch=<branch> always means refs/heads/<branch>
  clone: refuse to clone if --branch points to bogus ref
  clone: allow --branch to take a tag
  clone: print advice on checking out detached HEAD

 Documentation/git-clone.txt |    5 +-
 advice.c                    |   14 ++
 advice.h                    |    1 +
 builtin/checkout.c          |   16 +--
 builtin/clone.c             |  297 +++++++++++++++++++++++++------------------
 t/t5500-fetch-pack.sh       |   22 ++-
 t/t5601-clone.sh            |   40 +++++-
 t/t5706-clone-branch.sh     |    8 +-
 transport.c                 |    5 +-
 9 files changed, 249 insertions(+), 159 deletions(-)

-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v5 01/10] t5601: add missing && cascade
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
  2012-01-13 19:52       ` Junio C Hamano
  2012-01-16  9:46       ` [PATCH v5 00/10] nd/clone-detached rebase onto nd/clone-single-branch Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
                         ` (8 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t5601-clone.sh |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 87ee016..49821eb 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -9,9 +9,9 @@ test_expect_success setup '
 	rm -fr .git &&
 	test_create_repo src &&
 	(
-		cd src
-		>file
-		git add file
+		cd src &&
+		>file &&
+		git add file &&
 		git commit -m initial
 	)
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 02/10] clone: write detached HEAD in bare repositories
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (2 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
                         ` (7 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

If we don't write, HEAD is still at refs/heads/master as initialized
by init-db, which may or may not match remote's HEAD.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c  |    9 +++------
 t/t5601-clone.sh |   25 ++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 9dcc5fe..91862f7 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -764,12 +764,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		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;
-		}
+		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)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 49821eb..e0b8db6 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -12,7 +12,10 @@ test_expect_success setup '
 		cd src &&
 		>file &&
 		git add file &&
-		git commit -m initial
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 03/10] clone: factor out checkout code
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (3 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
                         ` (6 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Read HEAD from disk instead of relying on local variable
our_head_points_at, so that if earlier code fails to make HEAD
properly, it'll be detected.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 91862f7..98e3542 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,63 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (strcmp(head, "HEAD")) {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity > 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
 	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
@@ -766,13 +823,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		/* Source had detached HEAD pointing somewhere. */
 		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)
-			warning(_("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n"));
-		option_no_checkout = 1;
 	}
 
 	if (transport) {
@@ -780,42 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_disconnect(transport);
 	}
 
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = (option_verbosity > 0);
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		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);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die(_("unable to write new index file"));
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(our_head_points_at->old_sha1), "1",
-				NULL);
-
-		if (!err && option_recursive)
-			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
-	}
+	err = checkout();
 
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 04/10] clone: factor out HEAD update code
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (4 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
                         ` (5 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

While at it, update the comment at "if (remote_head)"

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   41 ++++++++++++++++++++++++-----------------
 1 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 98e3542..3b68014 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	if (our) {
+		/* Local default branch link */
+		create_symref("HEAD", our->name, NULL);
+		if (!option_bare) {
+			const char *head = skip_prefix(our->name, "refs/heads/");
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (remote) {
+		/*
+		 * We know remote HEAD points to a non-branch, or
+		 * HEAD points to a branch but we don't know which one, or
+		 * we asked for a specific branch but it did not exist.
+		 * Detach HEAD in all these cases.
+		 */
+		update_ref(msg, "HEAD", remote->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	}
+}
+
 static int checkout(void)
 {
 	unsigned char sha1[20];
@@ -807,23 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", our_head_points_at->name, NULL);
-		if (!option_bare) {
-			const char *head = skip_prefix(our_head_points_at->name,
-						       "refs/heads/");
-			update_ref(reflog_msg.buf, "HEAD",
-				   our_head_points_at->old_sha1,
-				   NULL, 0, DIE_ON_ERR);
-			install_branch_config(0, head, option_origin,
-					      our_head_points_at->name);
-		}
-	} else if (remote_head) {
-		/* Source had detached HEAD pointing somewhere. */
-		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
-	}
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
 	if (transport) {
 		transport_unlock_pack(transport);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 05/10] clone: factor out remote ref writing
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (5 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
                         ` (4 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   39 +++++++++++++++++++++++++--------------
 1 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 3b68014..2733fa4 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg)
+{
+	if (refs) {
+		clear_extra_refs();
+		write_remote_refs(mapped_refs);
+		if (option_single_branch)
+			write_followtags(refs, msg);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      msg);
+	}
+}
+
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
@@ -782,12 +805,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	}
 
 	if (refs) {
-		clear_extra_refs();
-
-		write_remote_refs(mapped_refs);
-		if (option_single_branch)
-			write_followtags(refs, reflog_msg.buf);
-
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
 			guess_remote_head(remote_head, mapped_refs, 0);
@@ -821,14 +838,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
-	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);
-	}
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (6 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
                         ` (3 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

This gives us an opportunity to abort the command during remote HEAD
check without wasting much bandwidth.

Cloning with remote-helper remains before the check because the remote
helper updates mapped_refs, which is necessary for remote ref checks.
foreign_vcs field is used to indicate the transport is handled by
remote helper.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------
 transport.c     |    5 ++++-
 2 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 2733fa4..a1fbb3b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -364,13 +364,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 	closedir(dir);
 }
 
-static const struct ref *clone_local(const char *src_repo,
-				     const char *dest_repo)
+static void clone_local(const char *src_repo, const char *dest_repo)
 {
-	const struct ref *ret;
-	struct remote *remote;
-	struct transport *transport;
-
 	if (option_shared) {
 		struct strbuf alt = STRBUF_INIT;
 		strbuf_addf(&alt, "%s/objects", src_repo);
@@ -386,13 +381,8 @@ static const struct ref *clone_local(const char *src_repo,
 		strbuf_release(&dest);
 	}
 
-	remote = remote_get(src_repo);
-	transport = transport_get(remote, src_repo);
-	ret = transport_get_remote_refs(transport);
-	transport_disconnect(transport);
 	if (0 <= option_verbosity)
 		printf(_("done.\n"));
-	return ret;
 }
 
 static const char *junk_work_tree;
@@ -619,6 +609,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
+	struct remote *remote;
 	int err = 0;
 
 	struct refspec *refspec;
@@ -773,13 +764,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	strbuf_reset(&value);
 
-	if (is_local) {
-		refs = clone_local(path, git_dir);
-		mapped_refs = wanted_peer_refs(refs, refspec);
-	} else {
-		struct remote *remote = remote_get(option_origin);
-		transport = transport_get(remote, remote->url[0]);
+	remote = remote_get(option_origin);
+	transport = transport_get(remote, remote->url[0]);
 
+	if (!is_local) {
 		if (!transport->get_refs_list || !transport->fetch)
 			die(_("Don't know how to clone %s"), transport->url);
 
@@ -796,14 +784,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
-
-		refs = transport_get_remote_refs(transport);
-		if (refs) {
-			mapped_refs = wanted_peer_refs(refs, refspec);
-			transport_fetch_refs(transport, mapped_refs);
-		}
 	}
 
+	refs = transport_get_remote_refs(transport);
+	mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
+
+	/*
+	 * mapped_refs may be updated if transport-helper is used so
+	 * we need fetch it early because remote_head code below
+	 * relies on it.
+	 *
+	 * for normal clones, transport_get_remote_refs() should
+	 * return reliable ref set, we can delay cloning until after
+	 * remote HEAD check.
+	 */
+	if (!is_local && remote->foreign_vcs && refs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	if (refs) {
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
@@ -838,15 +835,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && !remote->foreign_vcs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
 			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-	if (transport) {
-		transport_unlock_pack(transport);
-		transport_disconnect(transport);
-	}
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
 
 	err = checkout();
 
diff --git a/transport.c b/transport.c
index a99b7c9..4366639 100644
--- a/transport.c
+++ b/transport.c
@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		while (is_urlschemechar(p == url, *p))
 			p++;
-		if (!prefixcmp(p, "::"))
+		if (!prefixcmp(p, "::")) {
 			helper = xstrndup(url, p - url);
+			remote->foreign_vcs = helper;
+		}
 	}
 
 	if (helper) {
@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		char *handler = xmalloc(len + 1);
 		handler[len] = 0;
 		strncpy(handler, url, len);
+		remote->foreign_vcs = handler;
 		transport_helper_init(ret, handler);
 	}
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch>
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (7 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
                         ` (2 subsequent siblings)
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

It does not make sense to look outside refs/heads for HEAD's target
(src_ref_prefix can be set to "refs/" if --mirror is used) because ref
code only allows symref in form refs/heads/...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   30 ++++++++++++++++--------------
 1 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index a1fbb3b..253a794 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -48,7 +48,6 @@ static int option_verbosity;
 static int option_progress;
 static struct string_list option_config;
 static struct string_list option_reference;
-static const char *src_ref_prefix = "refs/heads/";
 
 static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
 {
@@ -413,6 +412,17 @@ static void remove_junk_on_signal(int signo)
 	raise(signo);
 }
 
+static struct ref *find_remote_branch(const struct ref *refs, const char *branch)
+{
+	struct ref *ref;
+	struct strbuf head = STRBUF_INIT;
+	strbuf_addstr(&head, "refs/heads/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+	return ref;
+}
+
 static struct ref *wanted_peer_refs(const struct ref *refs,
 		struct refspec *refspec)
 {
@@ -425,13 +435,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 
 		if (!option_branch)
 			remote_head = guess_remote_head(head, refs, 0);
-		else {
-			struct strbuf sb = STRBUF_INIT;
-			strbuf_addstr(&sb, src_ref_prefix);
-			strbuf_addstr(&sb, option_branch);
-			remote_head = find_ref_by_name(refs, sb.buf);
-			strbuf_release(&sb);
-		}
+		else
+			remote_head = find_remote_branch(refs, option_branch);
 
 		if (!remote_head && option_branch)
 			warning(_("Could not find remote branch %s to clone."),
@@ -502,7 +507,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
-	if (our) {
+	if (our && !prefixcmp(our->name, "refs/heads/")) {
 		/* Local default branch link */
 		create_symref("HEAD", our->name, NULL);
 		if (!option_bare) {
@@ -609,6 +614,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
+	const char *src_ref_prefix = "refs/heads/";
 	struct remote *remote;
 	int err = 0;
 
@@ -807,12 +813,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			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);
+				find_remote_branch(mapped_refs, option_branch);
 
 			if (!our_head_points_at) {
 				warning(_("Remote branch %s not found in "
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (8 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

It's possible that users make a typo in the branch name. Stop and let
users recheck. Falling back to remote's HEAD is not documented any
way.

Except when using remote helper, the pack has not been transferred at
this stage yet so we don't waste much bandwidth.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c         |   12 ++++--------
 t/t5500-fetch-pack.sh   |    7 -------
 t/t5706-clone-branch.sh |    8 ++------
 3 files changed, 6 insertions(+), 21 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 253a794..3cfedb3 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -518,8 +518,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
-		 * HEAD points to a branch but we don't know which one, or
-		 * we asked for a specific branch but it did not exist.
+		 * HEAD points to a branch but we don't know which one.
 		 * Detach HEAD in all these cases.
 		 */
 		update_ref(msg, "HEAD", remote->old_sha1,
@@ -816,12 +815,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			our_head_points_at =
 				find_remote_branch(mapped_refs, option_branch);
 
-			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;
-			}
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in upstream %s"),
+				    option_branch, option_origin);
 		}
 		else
 			our_head_points_at = remote_head_points_at;
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 7e85c71..5237066 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -282,13 +282,6 @@ test_expect_success 'clone shallow object count' '
 	test_cmp count3.expected count3.actual
 '
 
-test_expect_success 'clone shallow with nonexistent --branch' '
-	git clone --depth 1 --branch Z "file://$(pwd)/." shallow4 &&
-	GIT_DIR=shallow4/.git git rev-parse HEAD >actual &&
-	git rev-parse HEAD >expected &&
-	test_cmp expected actual
-'
-
 test_expect_success 'clone shallow with detached HEAD' '
 	git checkout HEAD^ &&
 	git clone --depth 1 "file://$(pwd)/." shallow5 &&
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
index f3f9a76..56be67e 100755
--- a/t/t5706-clone-branch.sh
+++ b/t/t5706-clone-branch.sh
@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
 	)
 '
 
-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_expect_success 'clone -b with bogus branch' '
+	test_must_fail git clone -b bogus parent clone-bogus
 '
 
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 09/10] clone: allow --branch to take a tag
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (9 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  2012-01-16  9:46       ` [PATCH v5 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Because a tag ref cannot be put to HEAD, HEAD will become detached.
This is consistent with "git checkout <tag>".

This is mostly useful in shallow clone, where it allows you to clone a
tag in addtion to branches.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clone.txt |    5 +++--
 builtin/clone.c             |   20 +++++++++++++++++++-
 t/t5500-fetch-pack.sh       |   15 +++++++++++++++
 t/t5601-clone.sh            |    9 +++++++++
 4 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 0931a3e..6e22522 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -147,8 +147,9 @@ objects from the source repository into a pack in the cloned repository.
 -b <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
-	instead. In a non-bare repository, this is the branch that will
-	be checked out.
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
diff --git a/builtin/clone.c b/builtin/clone.c
index 3cfedb3..651b4cc 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -420,6 +420,15 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
 	strbuf_addstr(&head, branch);
 	ref = find_ref_by_name(refs, head.buf);
 	strbuf_release(&head);
+
+	if (ref)
+		return ref;
+
+	strbuf_addstr(&head, "refs/tags/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
 	return ref;
 }
 
@@ -441,8 +450,12 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 		if (!remote_head && option_branch)
 			warning(_("Could not find remote branch %s to clone."),
 				option_branch);
-		else
+		else {
 			get_fetch_map(remote_head, refspec, &tail, 0);
+
+			/* if --branch=tag, pull the requested tag explicitly */
+			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+		}
 	} else
 		get_fetch_map(refs, refspec, &tail, 0);
 
@@ -515,6 +528,11 @@ static void update_head(const struct ref *our, const struct ref *remote,
 			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
 			install_branch_config(0, head, option_origin, our->name);
 		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(our->old_sha1);
+		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
+		update_ref(msg, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 5237066..ce51692 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -311,4 +311,19 @@ EOF
 	test_cmp count6.expected count6.actual
 '
 
+test_expect_success 'shallow cloning single tag' '
+	git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 &&
+	cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+	GIT_DIR=shallow7/.git git tag -l >taglist.actual &&
+	test_cmp taglist.expected taglist.actual &&
+
+	echo "in-pack: 7" > count7.expected &&
+	GIT_DIR=shallow7/.git git count-objects -v |
+		grep "^in-pack" > count7.actual &&
+	test_cmp count7.expected count7.actual
+'
+
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e0b8db6..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
 	grep /src/\\.git/objects target-10/objects/info/alternates
 '
 
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v5 10/10] clone: print advice on checking out detached HEAD
  2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
                         ` (10 preceding siblings ...)
  2012-01-16  9:46       ` [PATCH v5 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
@ 2012-01-16  9:46       ` Nguyễn Thái Ngọc Duy
  11 siblings, 0 replies; 68+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c           |   14 ++++++++++++++
 advice.h           |    1 +
 builtin/checkout.c |   16 +---------------
 builtin/clone.c    |    5 ++++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/advice.c b/advice.c
index e02e632..3e1a145 100644
--- a/advice.c
+++ b/advice.c
@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)
 	error_resolve_conflict(me);
 	die("Exiting because of an unresolved conflict.");
 }
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
index e5d0af7..7bda45b 100644
--- a/advice.h
+++ b/advice.h
@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1984d9..5bf96ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)
 	strbuf_release(&sb);
 }
 
-static void detach_advice(const char *old_path, const char *new_name)
-{
-	const char fmt[] =
-	"Note: checking out '%s'.\n\n"
-	"You are in 'detached HEAD' state. You can look around, make experimental\n"
-	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
-	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b new_branch_name\n\n";
-
-	fprintf(stderr, fmt, new_name);
-}
-
 static void update_refs_for_switch(struct checkout_opts *opts,
 				   struct branch_info *old,
 				   struct branch_info *new)
@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
-				detach_advice(old->path, new->name);
+				detach_advice(new->name);
 			describe_detached_head(_("HEAD is now at"), new->commit);
 		}
 	} else if (new->path) {	/* Switch branches. */
diff --git a/builtin/clone.c b/builtin/clone.c
index 651b4cc..72eebca 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -563,7 +563,10 @@ static int checkout(void)
 			  "unable to checkout.\n"));
 		return 0;
 	}
-	if (strcmp(head, "HEAD")) {
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
 		if (prefixcmp(head, "refs/heads/"))
 			die(_("HEAD not found below refs/heads!"));
 	}
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-10  9:57   ` [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
  2012-01-11 19:36     ` Junio C Hamano
@ 2012-01-24  0:15     ` Junio C Hamano
  2012-01-24  0:34       ` Junio C Hamano
  1 sibling, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-24  0:15 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

I wanted to merge this series to 'next' and further merge it early in the
next cycle to 'master', but with this my pushes (!!) seem to fail.

It breaks pushing to multiple URLs like this:

    $ cat .git/config
    [remote "origin"]
            url = https://code.google.com/p/git-htmldocs/
            url = github.com:gitster/git-htmldocs.git
            push = refs/heads/master:refs/heads/master
    $ git push

The second url that is supposed to use the git-over-ssh transport
mistakenly use https:// and fails with:

    error: Couldn't resolve host 'github.com:gitster' while accessing
    github.com:gitster/git-htmldocs.git/info/refs
    fatal: HTTP request failed

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-24  0:15     ` Junio C Hamano
@ 2012-01-24  0:34       ` Junio C Hamano
  2012-01-24  0:39         ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-24  0:34 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Junio C Hamano <gitster@pobox.com> writes:

> It breaks pushing to multiple URLs like this:
>
>     $ cat .git/config
>     [remote "origin"]
>             url = https://code.google.com/p/git-htmldocs/
>             url = github.com:gitster/git-htmldocs.git
>             push = refs/heads/master:refs/heads/master
>     $ git push
>
> The second url that is supposed to use the git-over-ssh transport
> mistakenly use https:// and fails with:
>
>     error: Couldn't resolve host 'github.com:gitster' while accessing
>     github.com:gitster/git-htmldocs.git/info/refs
>     fatal: HTTP request failed

And here is an obvious band-aid to work it around.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-24  0:34       ` Junio C Hamano
@ 2012-01-24  0:39         ` Junio C Hamano
  2012-01-24  7:01           ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2012-01-24  0:39 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jeff King

Junio C Hamano <gitster@pobox.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> It breaks pushing to multiple URLs like this:
>>
>>     $ cat .git/config
>>     [remote "origin"]
>>             url = https://code.google.com/p/git-htmldocs/
>>             url = github.com:gitster/git-htmldocs.git
>>             push = refs/heads/master:refs/heads/master
>>     $ git push
>>
>> The second url that is supposed to use the git-over-ssh transport
>> mistakenly use https:// and fails with:
>>
>>     error: Couldn't resolve host 'github.com:gitster' while accessing
>>     github.com:gitster/git-htmldocs.git/info/refs
>>     fatal: HTTP request failed
>
> And here is an obvious band-aid to work it around.

-- >8 --
Subject: [PATCH] push: do not let configured foreign-vcs permanently clobbered

Recently, 6f48d39 (clone: delay cloning until after remote HEAD checking,
2012-01-16) tried to record if a remote helper needs to be called after
parsing the remote when transport_get() is called, by overwriting the
field meant to store the configured remote helper name in the remote
structure.

This is OK when a remote represents a single remote repository, but fails
miserably when pushing to locations with multiple URLs, like this:

    $ cat .git/config
    [remote "origin"]
        url = https://code.google.com/p/git-htmldocs/
        url = github.com:gitster/git-htmldocs.git
        push = refs/heads/master:refs/heads/master
    $ git push

The second url that is supposed to use the git-over-ssh transport
mistakenly use https:// and fails with:

    error: Couldn't resolve host 'github.com:gitster' while accessing
    github.com:gitster/git-htmldocs.git/info/refs
    fatal: HTTP request failed

The right solution would probably be to dedicate a separate field to store
the detected external helper to be used, which is valid only during a
single use of transport until it is disconnected, instead of overwriting
foreign_vcs field, but in the meantime, this band-aid should suffice.

Signed-off-by: Junio C Hamano <gitster@pobox.com>

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/push.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/builtin/push.c b/builtin/push.c
index 35cce53..5fb98a0 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -204,11 +204,13 @@ static int do_push(const char *repo, int flags)
 		url_nr = remote->url_nr;
 	}
 	if (url_nr) {
+		const char *configured_foreign_vcs = remote->foreign_vcs;
 		for (i = 0; i < url_nr; i++) {
 			struct transport *transport =
 				transport_get(remote, url[i]);
 			if (push_with_options(transport, flags))
 				errs++;
+			remote->foreign_vcs = configured_foreign_vcs;
 		}
 	} else {
 		struct transport *transport =
-- 
1.7.9.rc2.100.gfd863d

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking
  2012-01-24  0:39         ` Junio C Hamano
@ 2012-01-24  7:01           ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 68+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2012-01-24  7:01 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

2012/1/24 Junio C Hamano <gitster@pobox.com>:
> Recently, 6f48d39 (clone: delay cloning until after remote HEAD checking,
> 2012-01-16) tried to record if a remote helper needs to be called after
> parsing the remote when transport_get() is called, by overwriting the
> field meant to store the configured remote helper name in the remote
> structure.
>
> This is OK when a remote represents a single remote repository, but fails
> miserably when pushing to locations with multiple URLs, like this:
>
>    $ cat .git/config
>    [remote "origin"]
>        url = https://code.google.com/p/git-htmldocs/
>        url = github.com:gitster/git-htmldocs.git
>        push = refs/heads/master:refs/heads/master

My bad. remote.*.vcs (or remote->foreign_vcs) is designed to override
remote helper detection based on url. Once you have pushed over https,
the following urls will be over https too. Luckily no other places
perform an operation over multiple urls like push so we're safe with
your bandage.

We should have another way to detect whether remote helper being used
(comparing some function pointers like transport->connect or
transport->disconnect may help). But I need to think a bit about
delaying cloning for http(s) too even though remote helper is used.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2012-01-24  7:02 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-05 13:49 [PATCH] clone: allow detached checkout when --branch takes a tag Nguyễn Thái Ngọc Duy
2012-01-05 14:18 ` Jeff King
2012-01-06 11:09   ` Nguyen Thai Ngoc Duy
2012-01-06 14:42     ` Jeff King
2012-01-05 16:22 ` Junio C Hamano
2012-01-06  7:35   ` Nguyen Thai Ngoc Duy
2012-01-08 11:46 ` [PATCH v2 1/6] t5601: add && Nguyễn Thái Ngọc Duy
2012-01-08 11:46   ` [PATCH v2 2/6] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
2012-01-08 11:46   ` [PATCH v2 3/6] clone: factor out checkout code Nguyễn Thái Ngọc Duy
2012-01-10  0:32     ` Junio C Hamano
2012-01-10  2:01       ` Nguyen Thai Ngoc Duy
2012-01-10  4:59         ` Junio C Hamano
2012-01-10  5:57           ` Nguyen Thai Ngoc Duy
2012-01-08 11:46   ` [PATCH v2 4/6] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
2012-01-10  0:33     ` Junio C Hamano
2012-01-08 11:46   ` [PATCH v2 5/6] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
2012-01-08 11:46   ` [PATCH v2 6/6] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
2012-01-10  0:36     ` Junio C Hamano
2012-01-10  1:54       ` Nguyen Thai Ngoc Duy
2012-01-10  4:49         ` Junio C Hamano
2012-01-10  5:54           ` Nguyen Thai Ngoc Duy
2012-01-10  9:56   ` [PATCH v3 00/10] nd/clone-detached Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 " Nguyễn Thái Ngọc Duy
2012-01-13 19:52       ` Junio C Hamano
2012-01-14  4:48         ` Nguyen Thai Ngoc Duy
2012-01-14  6:53           ` Junio C Hamano
2012-01-14  7:40             ` Nguyen Thai Ngoc Duy
2012-01-15  2:34               ` Junio C Hamano
2012-01-16  9:46       ` [PATCH v5 00/10] nd/clone-detached rebase onto nd/clone-single-branch Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
2012-01-16  9:46       ` [PATCH v5 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
2012-01-13  7:21     ` [PATCH v4 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
2012-01-13  7:22     ` [PATCH v4 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
2012-01-13  7:22     ` [PATCH v4 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
2012-01-13  7:22     ` [PATCH v4 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy
2012-01-10  9:56   ` [PATCH v3 01/10] t5601: add missing && cascade Nguyễn Thái Ngọc Duy
2012-01-10  9:56   ` [PATCH v3 02/10] clone: write detached HEAD in bare repositories Nguyễn Thái Ngọc Duy
2012-01-10  9:57   ` [PATCH v3 03/10] clone: factor out checkout code Nguyễn Thái Ngọc Duy
2012-01-10  9:57   ` [PATCH v3 04/10] clone: factor out HEAD update code Nguyễn Thái Ngọc Duy
2012-01-10  9:57   ` [PATCH v3 05/10] clone: factor out remote ref writing Nguyễn Thái Ngọc Duy
2012-01-10  9:57   ` [PATCH v3 06/10] clone: delay cloning until after remote HEAD checking Nguyễn Thái Ngọc Duy
2012-01-11 19:36     ` Junio C Hamano
2012-01-12  1:27       ` Nguyen Thai Ngoc Duy
2012-01-24  0:15     ` Junio C Hamano
2012-01-24  0:34       ` Junio C Hamano
2012-01-24  0:39         ` Junio C Hamano
2012-01-24  7:01           ` Nguyen Thai Ngoc Duy
2012-01-10  9:57   ` [PATCH v3 07/10] clone: --branch=<branch> always means refs/heads/<branch> Nguyễn Thái Ngọc Duy
2012-01-11 20:01     ` Junio C Hamano
2012-01-12  3:00       ` Nguyen Thai Ngoc Duy
2012-01-10  9:57   ` [PATCH v3 08/10] clone: refuse to clone if --branch points to bogus ref Nguyễn Thái Ngọc Duy
2012-01-10  9:57   ` [PATCH v3 09/10] clone: allow --branch to take a tag Nguyễn Thái Ngọc Duy
2012-01-11 23:57     ` Junio C Hamano
2012-01-10  9:57   ` [PATCH v3 10/10] clone: print advice on checking out detached HEAD Nguyễn Thái Ngọc Duy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).