Imported Upstream version 2.28.0
[platform/upstream/git.git] / builtin / clone.c
index f665b28..bef7074 100644 (file)
@@ -32,7 +32,6 @@
 #include "connected.h"
 #include "packfile.h"
 #include "list-objects-filter-options.h"
-#include "object-store.h"
 
 /*
  * Overall FIXMEs:
@@ -60,6 +59,7 @@ static const char *real_git_dir;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
+static int option_sparse_checkout;
 static enum transport_family family;
 static struct string_list option_config = STRING_LIST_INIT_NODUP;
 static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
@@ -102,10 +102,10 @@ 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_ALIAS(0, "recursive", "recurse-submodules"),
        { 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_ALIAS(0, "recursive", "recurse-submodules"),
        OPT_INTEGER('j', "jobs", &max_jobs,
                    N_("number of submodules cloned in parallel")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
@@ -147,6 +147,8 @@ static struct option builtin_clone_options[] = {
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
                    N_("any cloned submodules will use their remote-tracking branch")),
+       OPT_BOOL(0, "sparse", &option_sparse_checkout,
+                   N_("initialize sparse-checkout file to include only files at root")),
        OPT_END()
 };
 
@@ -418,6 +420,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
        struct dir_iterator *iter;
        int iter_status;
        unsigned int flags;
+       struct strbuf realpath = STRBUF_INIT;
 
        mkdir_if_missing(dest->buf, 0777);
 
@@ -452,7 +455,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                if (unlink(dest->buf) && errno != ENOENT)
                        die_errno(_("failed to unlink '%s'"), dest->buf);
                if (!option_no_hardlinks) {
-                       if (!link(real_path(src->buf), dest->buf))
+                       strbuf_realpath(&realpath, src->buf, 1);
+                       if (!link(realpath.buf, dest->buf))
                                continue;
                        if (option_local > 0)
                                die_errno(_("failed to create link '%s'"), dest->buf);
@@ -466,6 +470,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                strbuf_setlen(src, src_len);
                die(_("failed to iterate over '%s'"), src->buf);
        }
+
+       strbuf_release(&realpath);
 }
 
 static void clone_local(const char *src_repo, const char *dest_repo)
@@ -637,7 +643,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
                        continue;
                if (ends_with(ref->name, "^{}"))
                        continue;
-               if (!has_object_file(&ref->old_oid))
+               if (!has_object_file_with_flags(&ref->old_oid,
+                                               OBJECT_INFO_QUICK |
+                                               OBJECT_INFO_SKIP_FETCH_OBJECT))
                        continue;
                update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
@@ -670,8 +678,7 @@ static void update_remote_refs(const struct ref *refs,
                               const char *branch_top,
                               const char *msg,
                               struct transport *transport,
-                              int check_connectivity,
-                              int check_refs_only)
+                              int check_connectivity)
 {
        const struct ref *rm = mapped_refs;
 
@@ -680,7 +687,6 @@ static void update_remote_refs(const struct ref *refs,
 
                opt.transport = transport;
                opt.progress = transport->progress;
-               opt.check_refs_only = !!check_refs_only;
 
                if (check_connected(iterate_ref_map, &rm, &opt))
                        die(_("remote did not send all necessary objects"));
@@ -734,6 +740,27 @@ static void update_head(const struct ref *our, const struct ref *remote,
        }
 }
 
+static int git_sparse_checkout_init(const char *repo)
+{
+       struct argv_array argv = ARGV_ARRAY_INIT;
+       int result = 0;
+       argv_array_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+
+       /*
+        * We must apply the setting in the current process
+        * for the later checkout to use the sparse-checkout file.
+        */
+       core_apply_sparse_checkout = 1;
+
+       if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+               error(_("failed to initialize sparse-checkout"));
+               result = 1;
+       }
+
+       argv_array_clear(&argv);
+       return result;
+}
+
 static int checkout(int submodule_progress)
 {
        struct object_id oid;
@@ -756,11 +783,11 @@ static int checkout(int submodule_progress)
        if (!strcmp(head, "HEAD")) {
                if (advice_detached_head)
                        detach_advice(oid_to_hex(&oid));
+               FREE_AND_NULL(head);
        } else {
                if (!starts_with(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();
@@ -775,6 +802,7 @@ static int checkout(int submodule_progress)
        opts.verbose_update = (option_verbosity >= 0);
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
+       init_checkout_metadata(&opts.meta, head, &oid, NULL);
 
        tree = parse_tree_indirect(&oid);
        parse_tree(tree);
@@ -782,15 +810,17 @@ static int checkout(int submodule_progress)
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
 
+       free(head);
+
        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),
+       err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
                           oid_to_hex(&oid), "1", NULL);
 
        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");
@@ -809,6 +839,11 @@ static int checkout(int submodule_progress)
                        argv_array_push(&args, "--no-fetch");
                }
 
+               if (option_single_branch >= 0)
+                       argv_array_push(&args, option_single_branch ?
+                                              "--single-branch" :
+                                              "--no-single-branch");
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@ -900,7 +935,7 @@ static void dissociate_from_references(void)
        free(alternates);
 }
 
-static int dir_exists(const char *path)
+static int path_exists(const char *path)
 {
        struct stat sb;
        return !stat(path, &sb);
@@ -910,7 +945,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 {
        int is_bundle = 0, is_local;
        const char *repo_name, *repo, *work_tree, *git_dir;
-       char *path, *dir;
+       char *path, *dir, *display_repo = NULL;
        int dest_exists;
        const struct ref *refs, *remote_head;
        const struct ref *remote_head_points_at;
@@ -928,8 +963,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
-       fetch_if_missing = 0;
-
        packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
@@ -967,10 +1000,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        path = get_repo_path(repo_name, &is_bundle);
        if (path)
                repo = absolute_pathdup(repo_name);
-       else if (!strchr(repo_name, ':'))
-               die(_("repository '%s' does not exist"), repo_name);
-       else
+       else if (strchr(repo_name, ':')) {
                repo = repo_name;
+               display_repo = transport_anonymize_url(repo);
+       } else
+               die(_("repository '%s' does not exist"), repo_name);
 
        /* no need to be strict, transport_set_option() will validate it again */
        if (option_depth && atoi(option_depth) < 1)
@@ -982,18 +1016,20 @@ 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 = dir_exists(dir);
+       dest_exists = path_exists(dir);
        if (dest_exists && !is_empty_dir(dir))
                die(_("destination path '%s' already exists and is not "
                        "an empty directory."), dir);
 
-       strbuf_addf(&reflog_msg, "clone: from %s", repo);
+       strbuf_addf(&reflog_msg, "clone: from %s",
+                   display_repo ? display_repo : repo);
+       free(display_repo);
 
        if (option_bare)
                work_tree = NULL;
        else {
                work_tree = getenv("GIT_WORK_TREE");
-               if (work_tree && dir_exists(work_tree))
+               if (work_tree && path_exists(work_tree))
                        die(_("working tree '%s' already exists."), work_tree);
        }
 
@@ -1021,7 +1057,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        if (real_git_dir) {
-               if (dir_exists(real_git_dir))
+               if (path_exists(real_git_dir))
                        junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
                junk_git_dir = real_git_dir;
        } else {
@@ -1075,7 +1111,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                }
        }
 
-       init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
+               INIT_DB_QUIET);
 
        if (real_git_dir)
                git_dir = real_git_dir;
@@ -1107,6 +1144,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
+       if (option_sparse_checkout && git_sparse_checkout_init(dir))
+               return 1;
+
        remote = remote_get(option_origin);
 
        strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
@@ -1160,13 +1200,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport->server_options = &server_options;
 
        if (filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
                transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                                    expanded_filter_spec.buf);
+                                    spec);
                transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-               strbuf_release(&expanded_filter_spec);
        }
 
        if (transport->smart_options && !deepen && !filter_options.choice)
@@ -1183,6 +1221,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        refs = transport_get_remote_refs(transport, &ref_prefixes);
 
        if (refs) {
+               int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+
+               /*
+                * Now that we know what algorithm the remote side is using,
+                * let's set ours to the same thing.
+                */
+               initialize_repository_version(hash_algo);
+               repo_set_hash_algo(the_repository, hash_algo);
+
                mapped_refs = wanted_peer_refs(refs, &remote->fetch);
                /*
                 * transport_get_remote_refs() may return refs with null sha-1
@@ -1229,9 +1276,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                remote_head_points_at = NULL;
                remote_head = NULL;
                option_no_checkout = 1;
-               if (!option_bare)
-                       install_branch_config(0, "master", option_origin,
-                                             "refs/heads/master");
+               if (!option_bare) {
+                       const char *branch = git_default_branch_name();
+                       char *ref = xstrfmt("refs/heads/%s", branch);
+
+                       install_branch_config(0, branch, option_origin, ref);
+                       free(ref);
+               }
        }
 
        write_refspec_config(src_ref_prefix, our_head_points_at,
@@ -1247,7 +1298,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
-                          !is_local, filter_options.choice);
+                          !is_local);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
@@ -1268,7 +1319,6 @@ 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);