From bafe763197cb6535e5b4956ffeaa4e26ebb10651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:07 +0700 Subject: [PATCH 01/12] t5601: add missing && cascade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t5601-clone.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 87ee01662..49821eb46 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 ) From 7f08c6858ecfdf29f9713cf3a0b3699192f01a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:08 +0700 Subject: [PATCH 02/12] clone: write detached HEAD in bare repositories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano --- 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 9dcc5fe77..91862f796 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 49821eb46..e0b8db6c5 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 .git' ' git clone --bare src && From c39852c18dc7efa794bbe20b10ecfe7038409727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:09 +0700 Subject: [PATCH 03/12] clone: factor out checkout code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano --- builtin/clone.c | 101 +++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 91862f796..98e354242 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); From f034d3549fa34e65ca09ee9ce721dcf43faefd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:10 +0700 Subject: [PATCH 04/12] clone: factor out HEAD update code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it, update the comment at "if (remote_head)" Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/clone.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 98e354242..3b6801499 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); From 960b7d1c62bb8010954890b090546382161ae731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:11 +0700 Subject: [PATCH 05/12] clone: factor out remote ref writing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/clone.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 3b6801499..2733fa47e 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); From 6f48d39fa4d3e364c81d3cbbbd656c804b9ec873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:12 +0700 Subject: [PATCH 06/12] clone: delay cloning until after remote HEAD checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano --- 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 2733fa47e..a1fbb3b90 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 a99b7c9c4..43666394d 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); } From 9e5850460164f49dc9ae47569838084f5572846d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:13 +0700 Subject: [PATCH 07/12] clone: --branch= always means refs/heads/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano --- builtin/clone.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index a1fbb3b90..253a7946c 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 " From 920b691fe4da8115f9b79901411c0cc5fff17efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:14 +0700 Subject: [PATCH 08/12] clone: refuse to clone if --branch points to bogus ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano --- 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 253a7946c..3cfedb3a9 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 7e85c71ad..523706614 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 f3f9a7605..56be67e07 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 From 5a7d5b683f869d3e3884a89775241afa515da9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:15 +0700 Subject: [PATCH 09/12] clone: allow --branch to take a tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because a tag ref cannot be put to HEAD, HEAD will become detached. This is consistent with "git checkout ". 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 Signed-off-by: Junio C Hamano --- 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 0931a3e39..6e22522c4 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 :: Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository's HEAD, point to `` 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 :: -u :: diff --git a/builtin/clone.c b/builtin/clone.c index 3cfedb3a9..651b4cc20 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 523706614..ce51692bb 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 e0b8db6c5..67869b481 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 From 2857093ba15982d21ff0d5a9fd65294ac895cb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 16 Jan 2012 16:46:16 +0700 Subject: [PATCH 10/12] clone: print advice on checking out detached HEAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- 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 e02e632df..3e1a145bf 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 e5d0af782..7bda45b83 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 f1984d993..5bf96ba4d 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 651b4cc20..72eebca53 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!")); } From dad0b3d8e5586ff8c901a6bd643ea9bfe55d75a7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 23 Jan 2012 16:34:22 -0800 Subject: [PATCH 11/12] 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 Signed-off-by: Junio C Hamano --- builtin/push.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/push.c b/builtin/push.c index 35cce532f..5fb98a009 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 = From 9049816140e75d3f7b15264e97e042f5ab0bf392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 24 Jan 2012 18:10:38 +0700 Subject: [PATCH 12/12] clone: fix up delay cloning conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6f48d39 (clone: delay cloning until after remote HEAD checking - 2012-01-16) allows us to perform some checks on remote refs before the actual cloning happens. But not all transport types support this. Remote helper with "import" capability will not return complete ref information until fetch is performed and therefore the clone cannot be delayed. foreign_vcs field in struct remote was used to detect this kind of transport and save the result. This is a mistake because foreign_vcs is designed to override url-based transport detection. As a result, if the same "struct transport *" object is used on many different urls and one of them attached remote transport, the following urls will be mistakenly attached to the same transport. This fault is worked around by dad0b3d (push: do not let configured foreign-vcs permanently clobbered - 2012-01-23) To fix this, detect incomplete refs from transport_get_remote_refs() by SHA-1. Incomplete ones must have null SHA-1 (*). Then revert changes related to foreign_cvs field in 6f48d39 and dad0b3d. A good thing from this change is that cloning smart http transport can also be delayed. Earlier it falls into the same category "remote transport, no delay". (*) Theoretically if one of the remote refs happens to have null SHA-1, it will trigger false alarm and the clone will not be delayed. But that chance may be too small for us to pay attention to. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/clone.c | 20 ++++++++++++++------ builtin/push.c | 2 -- transport.c | 5 +---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 72eebca53..379cafa63 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -631,12 +631,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) const struct ref *remote_head_points_at; const struct ref *our_head_points_at; struct ref *mapped_refs; + const struct ref *ref; 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; + int err = 0, complete_refs_before_fetch = 1; struct refspec *refspec; const char *fetch_pattern; @@ -816,15 +817,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix) 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. + * transport_get_remote_refs() may return refs with null sha-1 + * in mapped_refs (see struct transport->get_refs_list + * comment). In that case 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) + for (ref = refs; ref; ref = ref->next) + if (is_null_sha1(ref->old_sha1)) { + complete_refs_before_fetch = 0; + break; + } + + if (!is_local && !complete_refs_before_fetch && refs) transport_fetch_refs(transport, mapped_refs); if (refs) { @@ -856,7 +864,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (is_local) clone_local(path, git_dir); - else if (refs && !remote->foreign_vcs) + else if (refs && complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, diff --git a/builtin/push.c b/builtin/push.c index 5fb98a009..35cce532f 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -204,13 +204,11 @@ 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 = diff --git a/transport.c b/transport.c index 43666394d..a99b7c9c4 100644 --- a/transport.c +++ b/transport.c @@ -895,10 +895,8 @@ 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) { @@ -940,7 +938,6 @@ 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); }