Imported Upstream version 2.17.3
[platform/upstream/git.git] / builtin / clone.c
index 6c76a6e..09fbca1 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include "builtin.h"
+#include "config.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "fetch-pack.h"
@@ -24,6 +25,8 @@
 #include "remote.h"
 #include "run-command.h"
 #include "connected.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
 
 /*
  * Overall FIXMEs:
@@ -39,7 +42,8 @@ static const char * const builtin_clone_usage[] = {
 };
 
 static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
-static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
+static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_no_tags;
 static int option_shallow_submodules;
 static int deepen;
 static char *option_template, *option_depth, *option_since;
@@ -56,6 +60,22 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
 static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
 static int option_dissociate;
 static int max_jobs = -1;
+static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+static struct list_objects_filter_options filter_options;
+
+static int recurse_submodules_cb(const struct option *opt,
+                                const char *arg, int unset)
+{
+       if (unset)
+               string_list_clear((struct string_list *)opt->value, 0);
+       else if (arg)
+               string_list_append((struct string_list *)opt->value, arg);
+       else
+               string_list_append((struct string_list *)opt->value,
+                                  (const char *)opt->defval);
+
+       return 0;
+}
 
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
@@ -74,10 +94,13 @@ static struct option builtin_clone_options[] = {
                    N_("don't use local hardlinks, always copy")),
        OPT_BOOL('s', "shared", &option_shared,
                    N_("setup as shared repository")),
-       OPT_BOOL(0, "recursive", &option_recursive,
-                   N_("initialize submodules in the clone")),
-       OPT_BOOL(0, "recurse-submodules", &option_recursive,
-                   N_("initialize submodules in the clone")),
+       { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
+         N_("pathspec"), N_("initialize submodules in the clone"),
+         PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
+         (intptr_t)"." },
+       { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
+         N_("pathspec"), N_("initialize submodules in the clone"),
+         PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
        OPT_INTEGER('j', "jobs", &max_jobs,
                    N_("number of submodules cloned in parallel")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
@@ -99,9 +122,11 @@ static struct option builtin_clone_options[] = {
        OPT_STRING(0, "shallow-since", &option_since, N_("time"),
                    N_("create a shallow clone since a specific time")),
        OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
-                       N_("deepen history of shallow clone by excluding rev")),
+                       N_("deepen history of shallow clone, excluding rev")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
+       OPT_BOOL(0, "no-tags", &option_no_tags,
+                N_("don't clone any tags, and make later fetches not to follow them")),
        OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
                    N_("any cloned submodules will be shallow")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
@@ -112,6 +137,7 @@ static struct option builtin_clone_options[] = {
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
+       OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_END()
 };
 
@@ -170,7 +196,7 @@ static char *get_repo_path(const char *repo, int *is_bundle)
 
        strbuf_addstr(&path, repo);
        raw = get_repo_path_1(&path, is_bundle);
-       canon = raw ? xstrdup(absolute_path(raw)) : NULL;
+       canon = raw ? absolute_pathdup(raw) : NULL;
        strbuf_release(&path);
        return canon;
 }
@@ -339,7 +365,7 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
         * to turn entries with paths relative to the original
         * absolute, so that they can be used in the new repository.
         */
-       FILE *in = fopen(src->buf, "r");
+       FILE *in = xfopen(src->buf, "r");
        struct strbuf line = STRBUF_INIT;
 
        while (strbuf_getline(&line, in) != EOF) {
@@ -429,7 +455,8 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 {
        if (option_shared) {
                struct strbuf alt = STRBUF_INIT;
-               strbuf_addf(&alt, "%s/objects", src_repo);
+               get_common_dir(&alt, src_repo);
+               strbuf_addstr(&alt, "/objects");
                add_to_alternates_file(alt.buf);
                strbuf_release(&alt);
        } else {
@@ -449,7 +476,9 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 }
 
 static const char *junk_work_tree;
+static int junk_work_tree_flags;
 static const char *junk_git_dir;
+static int junk_git_dir_flags;
 static enum {
        JUNK_LEAVE_NONE,
        JUNK_LEAVE_REPO,
@@ -478,14 +507,14 @@ static void remove_junk(void)
 
        if (junk_git_dir) {
                strbuf_addstr(&sb, junk_git_dir);
-               remove_dir_recursively(&sb, 0);
+               remove_dir_recursively(&sb, junk_git_dir_flags);
                strbuf_reset(&sb);
        }
        if (junk_work_tree) {
                strbuf_addstr(&sb, junk_work_tree);
-               remove_dir_recursively(&sb, 0);
-               strbuf_reset(&sb);
+               remove_dir_recursively(&sb, junk_work_tree_flags);
        }
+       strbuf_release(&sb);
 }
 
 static void remove_junk_on_signal(int signo)
@@ -545,7 +574,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
        } else
                get_fetch_map(refs, refspec, &tail, 0);
 
-       if (!option_mirror && !option_single_branch)
+       if (!option_mirror && !option_single_branch && !option_no_tags)
                get_fetch_map(refs, tag_refspec, &tail, 0);
 
        return local_refs;
@@ -565,7 +594,7 @@ static void write_remote_refs(const struct ref *local_refs)
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
-               if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash,
+               if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid,
                                           0, NULL, &err))
                        die("%s", err.buf);
        }
@@ -587,12 +616,12 @@ static void write_followtags(const struct ref *refs, const char *msg)
                        continue;
                if (!has_object_file(&ref->old_oid))
                        continue;
-               update_ref(msg, ref->name, ref->old_oid.hash,
-                          NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
+                          UPDATE_REFS_DIE_ON_ERR);
        }
 }
 
-static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
 {
        struct ref **rm = cb_data;
        struct ref *ref = *rm;
@@ -607,7 +636,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1;
 
-       hashcpy(sha1, ref->old_oid.hash);
+       oidcpy(oid, &ref->old_oid);
        *rm = ref->next;
        return 0;
 }
@@ -634,7 +663,7 @@ static void update_remote_refs(const struct ref *refs,
 
        if (refs) {
                write_remote_refs(mapped_refs);
-               if (option_single_branch)
+               if (option_single_branch && !option_no_tags)
                        write_followtags(refs, msg);
        }
 
@@ -659,31 +688,31 @@ static void update_head(const struct ref *our, const struct ref *remote,
                if (create_symref("HEAD", our->name, NULL) < 0)
                        die(_("unable to update HEAD"));
                if (!option_bare) {
-                       update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
+                       update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
                        install_branch_config(0, head, option_origin, our->name);
                }
        } else if (our) {
-               struct commit *c = lookup_commit_reference(our->old_oid.hash);
+               struct commit *c = lookup_commit_reference(&our->old_oid);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
-               update_ref(msg, "HEAD", c->object.oid.hash,
-                          NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+                          UPDATE_REFS_DIE_ON_ERR);
        } 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.
                 * Detach HEAD in all these cases.
                 */
-               update_ref(msg, "HEAD", remote->old_oid.hash,
-                          NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
+               update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+                          UPDATE_REFS_DIE_ON_ERR);
        }
 }
 
 static int checkout(int submodule_progress)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        char *head;
-       struct lock_file *lock_file;
+       struct lock_file lock_file = LOCK_INIT;
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
@@ -692,7 +721,7 @@ static int checkout(int submodule_progress)
        if (option_no_checkout)
                return 0;
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
        if (!head) {
                warning(_("remote HEAD refers to nonexistent ref, "
                          "unable to checkout.\n"));
@@ -700,7 +729,7 @@ static int checkout(int submodule_progress)
        }
        if (!strcmp(head, "HEAD")) {
                if (advice_detached_head)
-                       detach_advice(sha1_to_hex(sha1));
+                       detach_advice(oid_to_hex(&oid));
        } else {
                if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
@@ -710,8 +739,7 @@ static int checkout(int submodule_progress)
        /* We need to be in the new work tree for the checkout */
        setup_work_tree();
 
-       lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
@@ -721,21 +749,21 @@ static int checkout(int submodule_progress)
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
-       tree = parse_tree_indirect(sha1);
+       tree = parse_tree_indirect(&oid);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
-       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
-                          sha1_to_hex(sha1), "1", NULL);
+                          oid_to_hex(&oid), "1", NULL);
 
-       if (!err && option_recursive) {
+       if (!err && (option_recurse_submodules.nr > 0)) {
                struct argv_array args = ARGV_ARRAY_INIT;
-               argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+               argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
 
                if (option_shallow_submodules == 1)
                        argv_array_push(&args, "--depth=1");
@@ -746,6 +774,9 @@ static int checkout(int submodule_progress)
                if (submodule_progress)
                        argv_array_push(&args, "--progress");
 
+               if (option_verbosity < 0)
+                       argv_array_push(&args, "--quiet");
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@ -755,7 +786,9 @@ static int checkout(int submodule_progress)
 
 static int write_one_config(const char *key, const char *value, void *data)
 {
-       return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0);
+       return git_config_set_multivar_gently(key,
+                                             value ? value : "true",
+                                             CONFIG_REGEX_NONE, 0);
 }
 
 static void write_config(struct string_list *config)
@@ -835,10 +868,15 @@ static void dissociate_from_references(void)
        free(alternates);
 }
 
+static int dir_exists(const char *path)
+{
+       struct stat sb;
+       return !stat(path, &sb);
+}
+
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
        int is_bundle = 0, is_local;
-       struct stat buf;
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
        int dest_exists;
@@ -858,6 +896,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        struct refspec *refspec;
        const char *fetch_pattern;
 
+       fetch_if_missing = 0;
+
        packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
@@ -894,7 +934,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        path = get_repo_path(repo_name, &is_bundle);
        if (path)
-               repo = xstrdup(absolute_path(repo_name));
+               repo = absolute_pathdup(repo_name);
        else if (!strchr(repo_name, ':'))
                die(_("repository '%s' does not exist"), repo_name);
        else
@@ -910,7 +950,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                dir = guess_dir_name(repo_name, is_bundle, option_bare);
        strip_trailing_slashes(dir);
 
-       dest_exists = !stat(dir, &buf);
+       dest_exists = dir_exists(dir);
        if (dest_exists && !is_empty_dir(dir))
                die(_("destination path '%s' already exists and is not "
                        "an empty directory."), dir);
@@ -921,7 +961,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                work_tree = NULL;
        else {
                work_tree = getenv("GIT_WORK_TREE");
-               if (work_tree && !stat(work_tree, &buf))
+               if (work_tree && dir_exists(work_tree))
                        die(_("working tree '%s' already exists."), work_tree);
        }
 
@@ -939,14 +979,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (safe_create_leading_directories_const(work_tree) < 0)
                        die_errno(_("could not create leading directories of '%s'"),
                                  work_tree);
-               if (!dest_exists && mkdir(work_tree, 0777))
+               if (dest_exists)
+                       junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               else if (mkdir(work_tree, 0777))
                        die_errno(_("could not create work tree dir '%s'"),
                                  work_tree);
                junk_work_tree = work_tree;
                set_git_work_tree(work_tree);
        }
 
-       junk_git_dir = real_git_dir ? real_git_dir : git_dir;
+       if (real_git_dir) {
+               if (dir_exists(real_git_dir))
+                       junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               junk_git_dir = real_git_dir;
+       } else {
+               if (dest_exists)
+                       junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               junk_git_dir = git_dir;
+       }
        if (safe_create_leading_directories_const(git_dir) < 0)
                die(_("could not create leading directories of '%s'"), git_dir);
 
@@ -957,7 +1007,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        fprintf(stderr, _("Cloning into '%s'...\n"), dir);
        }
 
-       if (option_recursive) {
+       if (option_recurse_submodules.nr > 0) {
+               struct string_list_item *item;
+               struct strbuf sb = STRBUF_INIT;
+
+               /* remove duplicates */
+               string_list_sort(&option_recurse_submodules);
+               string_list_remove_duplicates(&option_recurse_submodules, 0);
+
+               /*
+                * NEEDSWORK: In a multi-working-tree world, this needs to be
+                * set in the per-worktree config.
+                */
+               for_each_string_list_item(item, &option_recurse_submodules) {
+                       strbuf_addf(&sb, "submodule.active=%s",
+                                   item->string);
+                       string_list_append(&option_config,
+                                          strbuf_detach(&sb, NULL));
+               }
+
                if (option_required_reference.nr &&
                    option_optional_reference.nr)
                        die(_("clone --recursive is not compatible with "
@@ -999,6 +1067,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        git_config_set(key.buf, repo);
        strbuf_reset(&key);
 
+       if (option_no_tags) {
+               strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+               git_config_set(key.buf, "--no-tags");
+               strbuf_reset(&key);
+       }
+
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
@@ -1021,6 +1095,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        warning(_("--shallow-since is ignored in local clones; use file:// instead."));
                if (option_not.nr)
                        warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
+               if (filter_options.choice)
+                       warning(_("--filter is ignored in local clones; use file:// instead."));
                if (!access(mkpath("%s/shallow", path), F_OK)) {
                        if (option_local > 0)
                                warning(_("source repository is shallow, ignoring --local"));
@@ -1031,9 +1107,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                warning(_("--local is ignored"));
        transport->cloning = 1;
 
-       if (!transport->get_refs_list || (!is_local && !transport->fetch))
-               die(_("Don't know how to clone %s"), transport->url);
-
        transport_set_option(transport, TRANS_OPT_KEEP, "yes");
 
        if (option_depth)
@@ -1052,7 +1125,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
 
-       if (transport->smart_options && !deepen)
+       if (filter_options.choice) {
+               transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+                                    filter_options.filter_spec);
+               transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       }
+
+       if (transport->smart_options && !deepen && !filter_options.choice)
                transport->smart_options->check_self_contained_and_connected = 1;
 
        refs = transport_get_remote_refs(transport);
@@ -1112,13 +1191,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        write_refspec_config(src_ref_prefix, our_head_points_at,
                        remote_head_points_at, &branch_top);
 
+       if (filter_options.choice)
+               partial_clone_register("origin", &filter_options);
+
        if (is_local)
                clone_local(path, git_dir);
        else if (refs && complete_refs_before_fetch)
                transport_fetch_refs(transport, mapped_refs);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
-                          branch_top.buf, reflog_msg.buf, transport, !is_local);
+                          branch_top.buf, reflog_msg.buf, transport,
+                          !is_local && !filter_options.choice);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
@@ -1139,6 +1222,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        junk_mode = JUNK_LEAVE_REPO;
+       fetch_if_missing = 1;
        err = checkout(submodule_progress);
 
        strbuf_release(&reflog_msg);