clone: allow --branch to take a tag

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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Jan 16, 2012 at 16:46 UTC 5a7d5b683f869d3e3884a89775241afa515da9e7
4 files changed +46 -3
Documentation/git-clone.txt
+3 -2
@@ -147,8 +147,9 @@ objects from the source repository into a pack in the cloned repository.
147 -b <name>::
148 Instead of pointing the newly created HEAD to the branch pointed
149 to by the cloned repository's HEAD, point to `<name>` branch
150 - instead. In a non-bare repository, this is the branch that will
151 - be checked out.
150 + instead. `--branch` can also take tags and treat them like
151 + detached HEAD. In a non-bare repository, this is the branch
152 + that will be checked out.
153
154 --upload-pack <upload-pack>::
155 -u <upload-pack>::
builtin/clone.c
+19 -1
@@ -420,6 +420,15 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
420 strbuf_addstr(&head, branch);
421 ref = find_ref_by_name(refs, head.buf);
422 strbuf_release(&head);
423 +
424 + if (ref)
425 + return ref;
426 +
427 + strbuf_addstr(&head, "refs/tags/");
428 + strbuf_addstr(&head, branch);
429 + ref = find_ref_by_name(refs, head.buf);
430 + strbuf_release(&head);
431 +
432 return ref;
433 }
434
@@ -441,8 +450,12 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
450 if (!remote_head && option_branch)
451 warning(_("Could not find remote branch %s to clone."),
452 option_branch);
444 - else
453 + else {
454 get_fetch_map(remote_head, refspec, &tail, 0);
455 +
456 + /* if --branch=tag, pull the requested tag explicitly */
457 + get_fetch_map(remote_head, tag_refspec, &tail, 0);
458 + }
459 } else
460 get_fetch_map(refs, refspec, &tail, 0);
461
@@ -515,6 +528,11 @@ static void update_head(const struct ref *our, const struct ref *remote,
528 update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
529 install_branch_config(0, head, option_origin, our->name);
530 }
531 + } else if (our) {
532 + struct commit *c = lookup_commit_reference(our->old_sha1);
533 + /* --branch specifies a non-branch (i.e. tags), detach HEAD */
534 + update_ref(msg, "HEAD", c->object.sha1,
535 + NULL, REF_NODEREF, DIE_ON_ERR);
536 } else if (remote) {
537 /*
538 * We know remote HEAD points to a non-branch, or
t/t5500-fetch-pack.sh
+15
@@ -311,4 +311,19 @@ EOF
311 test_cmp count6.expected count6.actual
312 '
313
314 +test_expect_success 'shallow cloning single tag' '
315 + git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 &&
316 + cat >taglist.expected <<\EOF &&
317 +TAGB1
318 +TAGB2
319 +EOF
320 + GIT_DIR=shallow7/.git git tag -l >taglist.actual &&
321 + test_cmp taglist.expected taglist.actual &&
322 +
323 + echo "in-pack: 7" > count7.expected &&
324 + GIT_DIR=shallow7/.git git count-objects -v |
325 + grep "^in-pack" > count7.actual &&
326 + test_cmp count7.expected count7.actual
327 +'
328 +
329 test_done
t/t5601-clone.sh
+9
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
271 grep /src/\\.git/objects target-10/objects/info/alternates
272 '
273
274 +test_expect_success 'clone checking out a tag' '
275 + git clone --branch=some-tag src dst.tag &&
276 + GIT_DIR=src/.git git rev-parse some-tag >expected &&
277 + test_cmp expected dst.tag/.git/HEAD &&
278 + GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
279 + echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
280 + test_cmp fetch.expected fetch.actual
281 +'
282 +
283 test_done