@matheusbsbs / matheus-bagliano / commits / 7a4ee28f41
Fork 0

clone: add --branch option to select a different HEAD

We currently point the HEAD of a newly cloned repo to the same ref as the parent repo's HEAD. While a user can then "git checkout -b foo origin/foo" whichever branch they choose, it is more convenient and more efficient to tell clone which branch you want in the first place. Based on a patch by Kirill A. Korinskiy. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 26, 2009 at 15:05 UTC 7a4ee28f41270bf032d0dd0bfb17f601b9b3971a
3 files changed +122 -28
Documentation/git-clone.txt
+7
@@ -127,6 +127,13 @@ objects from the source repository into a pack in the cloned repository.
127 Instead of using the remote name 'origin' to keep track
128 of the upstream repository, use <name>.
129
130 +--branch <name>::
131 +-b <name>::
132 + Instead of pointing the newly created HEAD to the branch pointed
133 + to by the cloned repositoroy's HEAD, point to <name> branch
134 + instead. In a non-bare repository, this is the branch that will
135 + be checked out.
136 +
137 --upload-pack <upload-pack>::
138 -u <upload-pack>::
139 When given, and the repository to clone from is accessed
builtin-clone.c
+47 -28
@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;
41 static int option_local, option_no_hardlinks, option_shared;
42 static char *option_template, *option_reference, *option_depth;
43 static char *option_origin = NULL;
44 +static char *option_branch = NULL;
45 static char *option_upload_pack = "git-upload-pack";
46 static int option_verbose;
47
@@ -65,6 +66,8 @@ static struct option builtin_clone_options[] = {
66 "reference repository"),
67 OPT_STRING('o', "origin", &option_origin, "branch",
68 "use <branch> instead of 'origin' to track upstream"),
69 + OPT_STRING('b', "branch", &option_branch, "branch",
70 + "checkout <branch> instead of the remote's HEAD"),
71 OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
72 "path to git-upload-pack on the remote"),
73 OPT_STRING(0, "depth", &option_depth, "depth",
@@ -347,7 +350,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
350 const char *repo_name, *repo, *work_tree, *git_dir;
351 char *path, *dir;
352 int dest_exists;
350 - const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
353 + const struct ref *refs, *remote_head, *mapped_refs;
354 + const struct ref *remote_head_points_at;
355 + const struct ref *our_head_points_at;
356 struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
357 struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
358 struct transport *transport = NULL;
@@ -519,11 +524,31 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
524 mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
525
526 remote_head = find_ref_by_name(refs, "HEAD");
522 - head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
527 + remote_head_points_at =
528 + guess_remote_head(remote_head, mapped_refs, 0);
529 +
530 + if (option_branch) {
531 + struct strbuf head = STRBUF_INIT;
532 + strbuf_addstr(&head, src_ref_prefix);
533 + strbuf_addstr(&head, option_branch);
534 + our_head_points_at =
535 + find_ref_by_name(mapped_refs, head.buf);
536 + strbuf_release(&head);
537 +
538 + if (!our_head_points_at) {
539 + warning("Remote branch %s not found in "
540 + "upstream %s, using HEAD instead",
541 + option_branch, option_origin);
542 + our_head_points_at = remote_head_points_at;
543 + }
544 + }
545 + else
546 + our_head_points_at = remote_head_points_at;
547 }
548 else {
549 warning("You appear to have cloned an empty repository.");
526 - head_points_at = NULL;
550 + our_head_points_at = NULL;
551 + remote_head_points_at = NULL;
552 remote_head = NULL;
553 option_no_checkout = 1;
554 if (!option_bare)
@@ -531,41 +556,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
556 "refs/heads/master");
557 }
558
534 - if (head_points_at) {
535 - /* Local default branch link */
536 - create_symref("HEAD", head_points_at->name, NULL);
559 + if (remote_head_points_at && !option_bare) {
560 + struct strbuf head_ref = STRBUF_INIT;
561 + strbuf_addstr(&head_ref, branch_top.buf);
562 + strbuf_addstr(&head_ref, "HEAD");
563 + create_symref(head_ref.buf,
564 + remote_head_points_at->peer_ref->name,
565 + reflog_msg.buf);
566 + }
567
568 + if (our_head_points_at) {
569 + /* Local default branch link */
570 + create_symref("HEAD", our_head_points_at->name, NULL);
571 if (!option_bare) {
539 - struct strbuf head_ref = STRBUF_INIT;
540 - const char *head = head_points_at->name;
541 -
542 - if (!prefixcmp(head, "refs/heads/"))
543 - head += 11;
544 -
545 - /* Set up the initial local branch */
546 -
547 - /* Local branch initial value */
572 + const char *head = skip_prefix(our_head_points_at->name,
573 + "refs/heads/");
574 update_ref(reflog_msg.buf, "HEAD",
549 - head_points_at->old_sha1,
575 + our_head_points_at->old_sha1,
576 NULL, 0, DIE_ON_ERR);
551 -
552 - strbuf_addstr(&head_ref, branch_top.buf);
553 - strbuf_addstr(&head_ref, "HEAD");
554 -
555 - /* Remote branch link */
556 - create_symref(head_ref.buf,
557 - head_points_at->peer_ref->name,
558 - reflog_msg.buf);
559 -
577 install_branch_config(0, head, option_origin,
561 - head_points_at->name);
578 + our_head_points_at->name);
579 }
580 } else if (remote_head) {
581 /* Source had detached HEAD pointing somewhere. */
565 - if (!option_bare)
582 + if (!option_bare) {
583 update_ref(reflog_msg.buf, "HEAD",
584 remote_head->old_sha1,
585 NULL, REF_NODEREF, DIE_ON_ERR);
586 + our_head_points_at = remote_head;
587 + }
588 } else {
589 /* Nothing to checkout out */
590 if (!option_no_checkout)
@@ -597,7 +616,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
616 opts.src_index = &the_index;
617 opts.dst_index = &the_index;
618
600 - tree = parse_tree_indirect(remote_head->old_sha1);
619 + tree = parse_tree_indirect(our_head_points_at->old_sha1);
620 parse_tree(tree);
621 init_tree_desc(&t, tree->buffer, tree->size);
622 unpack_trees(1, &t, &opts);
t/t5706-clone-branch.sh new
+68
@@ -0,0 +1,68 @@
1 +#!/bin/sh
2 +
3 +test_description='clone --branch option'
4 +. ./test-lib.sh
5 +
6 +check_HEAD() {
7 + echo refs/heads/"$1" >expect &&
8 + git symbolic-ref HEAD >actual &&
9 + test_cmp expect actual
10 +}
11 +
12 +check_file() {
13 + echo "$1" >expect &&
14 + test_cmp expect file
15 +}
16 +
17 +test_expect_success 'setup' '
18 + mkdir parent &&
19 + (cd parent && git init &&
20 + echo one >file && git add file && git commit -m one &&
21 + git checkout -b two &&
22 + echo two >file && git add file && git commit -m two &&
23 + git checkout master)
24 +'
25 +
26 +test_expect_success 'vanilla clone chooses HEAD' '
27 + git clone parent clone &&
28 + (cd clone &&
29 + check_HEAD master &&
30 + check_file one
31 + )
32 +'
33 +
34 +test_expect_success 'clone -b chooses specified branch' '
35 + git clone -b two parent clone-two &&
36 + (cd clone-two &&
37 + check_HEAD two &&
38 + check_file two
39 + )
40 +'
41 +
42 +test_expect_success 'clone -b sets up tracking' '
43 + (cd clone-two &&
44 + echo origin >expect &&
45 + git config branch.two.remote >actual &&
46 + echo refs/heads/two >>expect &&
47 + git config branch.two.merge >>actual &&
48 + test_cmp expect actual
49 + )
50 +'
51 +
52 +test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
53 + (cd clone-two &&
54 + echo refs/remotes/origin/master >expect &&
55 + git symbolic-ref refs/remotes/origin/HEAD >actual &&
56 + test_cmp expect actual
57 + )
58 +'
59 +
60 +test_expect_success 'clone -b with bogus branch chooses HEAD' '
61 + git clone -b bogus parent clone-bogus &&
62 + (cd clone-bogus &&
63 + check_HEAD master &&
64 + check_file one
65 + )
66 +'
67 +
68 +test_done