fetch-pack.c: use oidset to check existence of loose object
[gitweb.git] / builtin / clone.c
index 2da71db10752bf95647052dcd5b7c4b28e7a50ac..101c27a593f4c64a735410f18bfcb46489728696 100644 (file)
@@ -26,6 +26,7 @@
 #include "run-command.h"
 #include "connected.h"
 #include "packfile.h"
+#include "list-objects-filter-options.h"
 
 /*
  * Overall FIXMEs:
@@ -60,6 +61,7 @@ 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)
@@ -135,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()
 };
 
@@ -473,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,
@@ -502,12 +507,12 @@ 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);
+               remove_dir_recursively(&sb, junk_work_tree_flags);
        }
        strbuf_release(&sb);
 }
@@ -863,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;
@@ -886,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);
@@ -938,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);
@@ -949,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);
        }
 
@@ -967,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);
 
@@ -1073,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"));
@@ -1101,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);
@@ -1161,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);
 
@@ -1188,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);