diff options
Diffstat (limited to 'builtin-clone.c')
-rw-r--r-- | builtin-clone.c | 196 |
1 files changed, 136 insertions, 60 deletions
diff --git a/builtin-clone.c b/builtin-clone.c index 880373f279..58bacbd552 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -38,19 +38,25 @@ static const char * const builtin_clone_usage[] = { }; static int option_quiet, option_no_checkout, option_bare, option_mirror; -static int option_local, option_no_hardlinks, option_shared; +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; +static int option_progress; static struct option builtin_clone_options[] = { OPT__QUIET(&option_quiet), OPT__VERBOSE(&option_verbose), + OPT_BOOLEAN(0, "progress", &option_progress, + "force progress reporting"), OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, "don't create a checkout"), OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"), - OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"), + { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL, + "create a bare repository", + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "mirror", &option_mirror, "create a mirror repository (implies bare)"), OPT_BOOLEAN('l', "local", &option_local, @@ -59,12 +65,16 @@ static struct option builtin_clone_options[] = { "don't use local hardlinks, always copy"), OPT_BOOLEAN('s', "shared", &option_shared, "setup as shared repository"), + OPT_BOOLEAN(0, "recursive", &option_recursive, + "initialize submodules in the clone"), OPT_STRING(0, "template", &option_template, "path", "path the template repository"), OPT_STRING(0, "reference", &option_reference, "repo", "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", @@ -73,6 +83,10 @@ static struct option builtin_clone_options[] = { OPT_END() }; +static const char *argv_submodule[] = { + "submodule", "update", "--init", "--recursive", NULL +}; + static char *get_repo_path(const char *repo, int *is_bundle) { static char *suffix[] = { "/.git", ".git", "" }; @@ -104,11 +118,12 @@ static char *get_repo_path(const char *repo, int *is_bundle) static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start; + char *dir; /* - * Strip trailing slashes and /.git + * Strip trailing spaces, slashes and /.git */ - while (repo < end && is_dir_sep(end[-1])) + while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; if (end - repo > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { @@ -140,10 +155,33 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) if (is_bare) { struct strbuf result = STRBUF_INIT; strbuf_addf(&result, "%.*s.git", (int)(end - start), start); - return strbuf_detach(&result, 0); + dir = strbuf_detach(&result, NULL); + } else + dir = xstrndup(start, end - start); + /* + * Replace sequences of 'control' characters and whitespace + * with one ascii space, remove leading and trailing spaces. + */ + if (*dir) { + char *out = dir; + int prev_space = 1 /* strip leading whitespace */; + for (end = dir; *end; ++end) { + char ch = *end; + if ((unsigned char)ch < '\x20') + ch = '\x20'; + if (isspace(ch)) { + if (prev_space) + continue; + prev_space = 1; + } else + prev_space = 0; + *out++ = ch; + } + *out = '\0'; + if (out > dir && prev_space) + out[-1] = '\0'; } - - return xstrndup(start, end - start); + return dir; } static void strip_trailing_slashes(char *dir) @@ -196,13 +234,13 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) dir = opendir(src->buf); if (!dir) - die("failed to open %s", src->buf); + die_errno("failed to open '%s'", src->buf); if (mkdir(dest->buf, 0777)) { if (errno != EEXIST) - die("failed to create directory %s", dest->buf); + die_errno("failed to create directory '%s'", dest->buf); else if (stat(dest->buf, &buf)) - die("failed to stat %s", dest->buf); + die_errno("failed to stat '%s'", dest->buf); else if (!S_ISDIR(buf.st_mode)) die("%s exists and is not a directory", dest->buf); } @@ -228,16 +266,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest) } if (unlink(dest->buf) && errno != ENOENT) - die("failed to unlink %s", dest->buf); + die_errno("failed to unlink '%s'", dest->buf); if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; if (option_local) - die("failed to create link %s", dest->buf); + die_errno("failed to create link '%s'", dest->buf); option_no_hardlinks = 1; } - if (copy_file(dest->buf, src->buf, 0666)) - die("failed to copy file to %s", dest->buf); + if (copy_file_with_time(dest->buf, src->buf, 0666)) + die_errno("failed to copy file to '%s'", dest->buf); } closedir(dir); } @@ -296,24 +334,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) @@ -323,7 +365,10 @@ 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; + const struct ref *remote_head_points_at; + const struct ref *our_head_points_at; + struct ref *mapped_refs; struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; @@ -335,11 +380,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix) junk_pid = getpid(); - argc = parse_options(argc, argv, builtin_clone_options, + argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); + if (argc > 2) + usage_msg_opt("Too many arguments.", + builtin_clone_usage, builtin_clone_options); + if (argc == 0) - die("You must specify a repository to clone."); + usage_msg_opt("You must specify a repository to clone.", + builtin_clone_usage, builtin_clone_options); if (option_mirror) option_bare = 1; @@ -395,11 +445,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (!option_bare) { junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) - die("could not create leading directories of '%s': %s", - work_tree, strerror(errno)); + die_errno("could not create leading directories of '%s'", + work_tree); if (!dest_exists && mkdir(work_tree, 0755)) - die("could not create work tree dir '%s': %s.", - work_tree, strerror(errno)); + die_errno("could not create work tree dir '%s'.", + work_tree); set_git_work_tree(work_tree); } junk_git_dir = git_dir; @@ -460,9 +510,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]); @@ -478,6 +529,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_quiet) transport->verbose = -1; else if (option_verbose) + transport->verbose = 1; + + if (option_progress) transport->progress = 1; if (option_upload_pack) @@ -485,21 +539,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) @@ -507,41 +583,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) @@ -550,8 +620,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_no_checkout = 1; } - if (transport) + if (transport) { transport_unlock_pack(transport); + transport_disconnect(transport); + } if (!option_no_checkout) { struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -573,7 +645,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); @@ -583,7 +655,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix) die("unable to write new index file"); err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), - sha1_to_hex(remote_head->old_sha1), "1", NULL); + 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); } strbuf_release(&reflog_msg); |