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