builtin-merge: show user-friendly error messages for fast-forward too.
[gitweb.git] / builtin-clone.c
index 0f231d8af5880d96f70729d89602960d8252f4a0..4992c2597c903d86ce1d0c209828aeb962fef23a 100644 (file)
@@ -41,6 +41,7 @@ static int option_quiet, option_no_checkout, option_bare, option_mirror;
 static int option_local, option_no_hardlinks, option_shared, option_recursive;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
+static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbose;
 
@@ -67,6 +68,8 @@ static struct option builtin_clone_options[] = {
                   "reference repository"),
        OPT_STRING('o', "origin", &option_origin, "branch",
                   "use <branch> instead of 'origin' to track upstream"),
+       OPT_STRING('b', "branch", &option_branch, "branch",
+                  "checkout <branch> instead of the remote's HEAD"),
        OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
@@ -266,7 +269,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
                                die_errno("failed to create link '%s'", dest->buf);
                        option_no_hardlinks = 1;
                }
-               if (copy_file(dest->buf, src->buf, 0666))
+               if (copy_file_with_time(dest->buf, src->buf, 0666))
                        die_errno("failed to copy file to '%s'", dest->buf);
        }
        closedir(dir);
@@ -326,24 +329,28 @@ static void remove_junk_on_signal(int signo)
        raise(signo);
 }
 
-static struct ref *write_remote_refs(const struct ref *refs,
-               struct refspec *refspec, const char *reflog)
+static struct ref *wanted_peer_refs(const struct ref *refs,
+               struct refspec *refspec)
 {
        struct ref *local_refs = NULL;
        struct ref **tail = &local_refs;
-       struct ref *r;
 
        get_fetch_map(refs, refspec, &tail, 0);
        if (!option_mirror)
                get_fetch_map(refs, tag_refspec, &tail, 0);
 
+       return local_refs;
+}
+
+static void write_remote_refs(const struct ref *local_refs)
+{
+       const struct ref *r;
+
        for (r = local_refs; r; r = r->next)
                add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
 
        pack_refs(PACK_REFS_ALL);
        clear_extra_refs();
-
-       return local_refs;
 }
 
 int cmd_clone(int argc, const char **argv, const char *prefix)
@@ -353,7 +360,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
        int dest_exists;
-       const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+       const struct ref *refs, *remote_head, *mapped_refs;
+       const struct ref *remote_head_points_at;
+       const struct ref *our_head_points_at;
        struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
@@ -490,9 +499,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        strbuf_reset(&value);
 
-       if (path && !is_bundle)
+       if (path && !is_bundle) {
                refs = clone_local(path, git_dir);
-       else {
+               mapped_refs = wanted_peer_refs(refs, refspec);
+       } else {
                struct remote *remote = remote_get(argv[0]);
                transport = transport_get(remote, remote->url[0]);
 
@@ -515,21 +525,43 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                             option_upload_pack);
 
                refs = transport_get_remote_refs(transport);
-               if (refs)
-                       transport_fetch_refs(transport, refs);
+               if (refs) {
+                       mapped_refs = wanted_peer_refs(refs, refspec);
+                       transport_fetch_refs(transport, mapped_refs);
+               }
        }
 
        if (refs) {
                clear_extra_refs();
 
-               mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
+               write_remote_refs(mapped_refs);
 
                remote_head = find_ref_by_name(refs, "HEAD");
-               head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+               remote_head_points_at =
+                       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);
+
+                       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;
+                       }
+               }
+               else
+                       our_head_points_at = remote_head_points_at;
        }
        else {
                warning("You appear to have cloned an empty repository.");
-               head_points_at = NULL;
+               our_head_points_at = NULL;
+               remote_head_points_at = NULL;
                remote_head = NULL;
                option_no_checkout = 1;
                if (!option_bare)
@@ -537,41 +569,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                              "refs/heads/master");
        }
 
-       if (head_points_at) {
-               /* Local default branch link */
-               create_symref("HEAD", head_points_at->name, NULL);
+       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);
+       }
 
+       if (our_head_points_at) {
+               /* Local default branch link */
+               create_symref("HEAD", our_head_points_at->name, NULL);
                if (!option_bare) {
-                       struct strbuf head_ref = STRBUF_INIT;
-                       const char *head = head_points_at->name;
-
-                       if (!prefixcmp(head, "refs/heads/"))
-                               head += 11;
-
-                       /* Set up the initial local branch */
-
-                       /* Local branch initial value */
+                       const char *head = skip_prefix(our_head_points_at->name,
+                                                      "refs/heads/");
                        update_ref(reflog_msg.buf, "HEAD",
-                                  head_points_at->old_sha1,
+                                  our_head_points_at->old_sha1,
                                   NULL, 0, DIE_ON_ERR);
-
-                       strbuf_addstr(&head_ref, branch_top.buf);
-                       strbuf_addstr(&head_ref, "HEAD");
-
-                       /* Remote branch link */
-                       create_symref(head_ref.buf,
-                                     head_points_at->peer_ref->name,
-                                     reflog_msg.buf);
-
                        install_branch_config(0, head, option_origin,
-                                             head_points_at->name);
+                                             our_head_points_at->name);
                }
        } else if (remote_head) {
                /* Source had detached HEAD pointing somewhere. */
-               if (!option_bare)
+               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;
+               }
        } else {
                /* Nothing to checkout out */
                if (!option_no_checkout)
@@ -605,7 +631,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                opts.src_index = &the_index;
                opts.dst_index = &the_index;
 
-               tree = parse_tree_indirect(remote_head->old_sha1);
+               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);