Merge branch 'sg/cocci-move-array' into next
[gitweb.git] / builtin / fetch.c
index c87e59f3b1def1f064e0dae54a14e310c06df1fa..8ee998ea2ee8f736a49afe285d31b0be478cf15b 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include "cache.h"
 #include "config.h"
+#include "repository.h"
 #include "refs.h"
 #include "commit.h"
 #include "builtin.h"
@@ -17,6 +18,8 @@
 #include "connected.h"
 #include "argv-array.h"
 #include "utf8.h"
+#include "packfile.h"
+#include "list-objects-filter-options.h"
 
 static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -39,7 +42,7 @@ static int prune = -1; /* unspecified */
 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 static int progress = -1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_children = -1;
+static int max_children = 1;
 static enum transport_family family;
 static const char *depth;
 static const char *deepen_since;
@@ -54,6 +57,7 @@ static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
 static int shown_url = 0;
 static int refmap_alloc, refmap_nr;
 static const char **refmap_array;
+static struct list_objects_filter_options filter_options;
 
 static int git_fetch_config(const char *k, const char *v, void *cb)
 {
@@ -68,9 +72,30 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
                recurse_submodules = r;
        }
 
+       if (!strcmp(k, "submodule.fetchjobs")) {
+               max_children = parse_submodule_fetchjobs(k, v);
+               return 0;
+       } else if (!strcmp(k, "fetch.recursesubmodules")) {
+               recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+               return 0;
+       }
+
        return git_default_config(k, v, cb);
 }
 
+static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "submodule.fetchjobs")) {
+               max_children = parse_submodule_fetchjobs(var, value);
+               return 0;
+       } else if (!strcmp(var, "fetch.recursesubmodules")) {
+               recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
+               return 0;
+       }
+
+       return 0;
+}
+
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
 {
        ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
@@ -138,6 +163,7 @@ static struct option builtin_fetch_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()
 };
 
@@ -435,8 +461,8 @@ static int s_update_ref(const char *action,
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_update(transaction, ref->name,
-                                  ref->new_oid.hash,
-                                  check_old ? ref->old_oid.hash : NULL,
+                                  &ref->new_oid,
+                                  check_old ? &ref->old_oid : NULL,
                                   0, msg, &err))
                goto fail;
 
@@ -705,7 +731,7 @@ static int update_local_ref(struct ref *ref,
        }
 }
 
-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;
@@ -715,7 +741,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1; /* end of the list */
        *rm = ref->next;
-       hashcpy(sha1, ref->old_oid.hash);
+       oidcpy(oid, &ref->old_oid);
        return 0;
 }
 
@@ -1022,6 +1048,11 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
                set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
+       if (filter_options.choice) {
+               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+                          filter_options.filter_spec);
+               set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       }
        return transport;
 }
 
@@ -1072,9 +1103,6 @@ static int do_fetch(struct transport *transport,
                        tags = TAGS_UNSET;
        }
 
-       if (!transport->get_refs_list || !transport->fetch)
-               die(_("Don't know how to fetch from %s"), transport->url);
-
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
                retcode = truncate_fetch_head();
@@ -1245,6 +1273,56 @@ static int fetch_multiple(struct string_list *list)
        return result;
 }
 
+/*
+ * Fetching from the promisor remote should use the given filter-spec
+ * or inherit the default filter-spec from the config.
+ */
+static inline void fetch_one_setup_partial(struct remote *remote)
+{
+       /*
+        * Explicit --no-filter argument overrides everything, regardless
+        * of any prior partial clones and fetches.
+        */
+       if (filter_options.no_filter)
+               return;
+
+       /*
+        * If no prior partial clone/fetch and the current fetch DID NOT
+        * request a partial-fetch, do a normal fetch.
+        */
+       if (!repository_format_partial_clone && !filter_options.choice)
+               return;
+
+       /*
+        * If this is the FIRST partial-fetch request, we enable partial
+        * on this repo and remember the given filter-spec as the default
+        * for subsequent fetches to this remote.
+        */
+       if (!repository_format_partial_clone && filter_options.choice) {
+               partial_clone_register(remote->name, &filter_options);
+               return;
+       }
+
+       /*
+        * We are currently limited to only ONE promisor remote and only
+        * allow partial-fetches from the promisor remote.
+        */
+       if (strcmp(remote->name, repository_format_partial_clone)) {
+               if (filter_options.choice)
+                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+               return;
+       }
+
+       /*
+        * Do a partial-fetch from the promisor remote using either the
+        * explicitly given filter-spec or inherit the filter-spec from
+        * the config.
+        */
+       if (!filter_options.choice)
+               partial_clone_get_default_filter_spec(&filter_options);
+       return;
+}
+
 static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
        static const char **refs = NULL;
@@ -1300,17 +1378,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
        struct string_list list = STRING_LIST_INIT_DUP;
-       struct remote *remote;
+       struct remote *remote = NULL;
        int result = 0;
        struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
 
        packet_trace_identity("fetch");
 
+       fetch_if_missing = 0;
+
        /* Record the command line for the reflog */
        strbuf_addstr(&default_rla, "fetch");
        for (i = 1; i < argc; i++)
                strbuf_addf(&default_rla, " %s", argv[i]);
 
+       config_from_gitmodules(gitmodules_fetch_config, NULL);
        git_config(git_fetch_config, NULL);
 
        argc = parse_options(argc, argv, prefix,
@@ -1338,11 +1419,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
-       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-               set_config_fetch_recurse_submodules(recurse_submodules_default);
-               gitmodules_config();
-               git_config(submodule_config, NULL);
-       }
+       if (filter_options.choice && !repository_format_partial_clone)
+               die("--filter can only be used when extensions.partialClone is set");
 
        if (all) {
                if (argc == 1)
@@ -1350,17 +1428,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
                (void) for_each_remote(get_one_remote_for_fetch, &list);
-               result = fetch_multiple(&list);
        } else if (argc == 0) {
                /* No arguments -- use default remote */
                remote = remote_get(NULL);
-               result = fetch_one(remote, argc, argv);
        } else if (multiple) {
                /* All arguments are assumed to be remotes or groups */
                for (i = 0; i < argc; i++)
                        if (!add_remote_or_group(argv[i], &list))
                                die(_("No such remote or remote group: %s"), argv[i]);
-               result = fetch_multiple(&list);
        } else {
                /* Single remote or group */
                (void) add_remote_or_group(argv[0], &list);
@@ -1368,21 +1443,34 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        /* More than one remote */
                        if (argc > 1)
                                die(_("Fetching a group and specifying refspecs does not make sense"));
-                       result = fetch_multiple(&list);
                } else {
                        /* Zero or one remotes */
                        remote = remote_get(argv[0]);
-                       result = fetch_one(remote, argc-1, argv+1);
+                       argc--;
+                       argv++;
                }
        }
 
+       if (remote) {
+               if (filter_options.choice || repository_format_partial_clone)
+                       fetch_one_setup_partial(remote);
+               result = fetch_one(remote, argc, argv);
+       } else {
+               if (filter_options.choice)
+                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+               /* TODO should this also die if we have a previous partial-clone? */
+               result = fetch_multiple(&list);
+       }
+
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                struct argv_array options = ARGV_ARRAY_INIT;
 
                add_options_to_argv(&options);
-               result = fetch_populated_submodules(&options,
+               result = fetch_populated_submodules(the_repository,
+                                                   &options,
                                                    submodule_prefix,
                                                    recurse_submodules,
+                                                   recurse_submodules_default,
                                                    verbosity < 0,
                                                    max_children);
                argv_array_clear(&options);