fetch-pack: write shallow, then check connectivity
[gitweb.git] / builtin / fetch.c
index 1f037e8e4b8ba41b66314c5dd9633e0164f86c0e..d4b2767d488950d6ed4153d35d3af2cb2b677d7d 100644 (file)
@@ -5,6 +5,7 @@
 #include "config.h"
 #include "repository.h"
 #include "refs.h"
+#include "refspec.h"
 #include "commit.h"
 #include "builtin.h"
 #include "string-list.h"
@@ -59,8 +60,7 @@ static const char *submodule_prefix = "";
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 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 refspec refmap = REFSPEC_INIT_FETCH;
 static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 
@@ -108,14 +108,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
 {
-       ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
-
        /*
         * "git fetch --refmap='' origin foo"
         * can be used to tell the command not to store anywhere
         */
-       if (*arg)
-               refmap_array[refmap_nr++] = arg;
+       refspec_append(&refmap, arg);
+
        return 0;
 }
 
@@ -157,9 +155,9 @@ static struct option builtin_fetch_options[] = {
                        N_("deepen history of shallow clone, excluding rev")),
        OPT_INTEGER(0, "deepen", &deepen_relative,
                    N_("deepen history of shallow clone")),
-       { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
-                  N_("convert to a complete repository"),
-                  PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
+       OPT_SET_INT_F(0, "unshallow", &unshallow,
+                     N_("convert to a complete repository"),
+                     1, PARSE_OPT_NONEG),
        { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
                   N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
        { OPTION_CALLBACK, 0, "recurse-submodules-default",
@@ -204,7 +202,7 @@ static void add_merge_config(struct ref **head,
 
        for (i = 0; i < branch->merge_nr; i++) {
                struct ref *rm, **old_tail = *tail;
-               struct refspec refspec;
+               struct refspec_item refspec;
 
                for (rm = *head; rm; rm = rm->next) {
                        if (branch_merge_matches(branch, i, rm->name)) {
@@ -256,9 +254,9 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
-static void find_non_local_tags(struct transport *transport,
-                       struct ref **head,
-                       struct ref ***tail)
+static void find_non_local_tags(const struct ref *refs,
+                               struct ref **head,
+                               struct ref ***tail)
 {
        struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct string_list remote_refs = STRING_LIST_INIT_NODUP;
@@ -266,7 +264,7 @@ static void find_non_local_tags(struct transport *transport,
        struct string_list_item *item = NULL;
 
        for_each_ref(add_existing, &existing_refs);
-       for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) {
+       for (ref = refs; ref; ref = ref->next) {
                if (!starts_with(ref->name, "refs/tags/"))
                        continue;
 
@@ -340,44 +338,27 @@ static void find_non_local_tags(struct transport *transport,
        string_list_clear(&remote_refs, 0);
 }
 
-static struct ref *get_ref_map(struct transport *transport,
-                              struct refspec *refspecs, int refspec_count,
+static struct ref *get_ref_map(struct remote *remote,
+                              const struct ref *remote_refs,
+                              struct refspec *rs,
                               int tags, int *autotags)
 {
        int i;
        struct ref *rm;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
-       struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
        /* opportunistically-updated references: */
        struct ref *orefs = NULL, **oref_tail = &orefs;
 
-       const struct ref *remote_refs;
-
-       for (i = 0; i < refspec_count; i++) {
-               if (!refspecs[i].exact_sha1) {
-                       const char *glob = strchr(refspecs[i].src, '*');
-                       if (glob)
-                               argv_array_pushf(&ref_prefixes, "%.*s",
-                                                (int)(glob - refspecs[i].src),
-                                                refspecs[i].src);
-                       else
-                               expand_ref_prefix(&ref_prefixes, refspecs[i].src);
-               }
-       }
-
-       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
-
-       argv_array_clear(&ref_prefixes);
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
 
-       if (refspec_count) {
+       if (rs->nr) {
                struct refspec *fetch_refspec;
-               int fetch_refspec_nr;
 
-               for (i = 0; i < refspec_count; i++) {
-                       get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
-                       if (refspecs[i].dst && refspecs[i].dst[0])
+               for (i = 0; i < rs->nr; i++) {
+                       get_fetch_map(remote_refs, &rs->items[i], &tail, 0);
+                       if (rs->items[i].dst && rs->items[i].dst[0])
                                *autotags = 1;
                }
                /* Merge everything on the command line (but not --tags) */
@@ -404,34 +385,30 @@ static struct ref *get_ref_map(struct transport *transport,
                 * by ref_remove_duplicates() in favor of one of these
                 * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
-               if (refmap_array) {
-                       fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
-                       fetch_refspec_nr = refmap_nr;
-               } else {
-                       fetch_refspec = transport->remote->fetch;
-                       fetch_refspec_nr = transport->remote->fetch_refspec_nr;
-               }
+               if (refmap.nr)
+                       fetch_refspec = &refmap;
+               else
+                       fetch_refspec = &remote->fetch;
 
-               for (i = 0; i < fetch_refspec_nr; i++)
-                       get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
-       } else if (refmap_array) {
+               for (i = 0; i < fetch_refspec->nr; i++)
+                       get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
+       } else if (refmap.nr) {
                die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
                /* Use the defaults */
-               struct remote *remote = transport->remote;
                struct branch *branch = branch_get(NULL);
                int has_merge = branch_has_merge_config(branch);
                if (remote &&
-                   (remote->fetch_refspec_nr ||
+                   (remote->fetch.nr ||
                     /* Note: has_merge implies non-NULL branch->remote_name */
                     (has_merge && !strcmp(branch->remote_name, remote->name)))) {
-                       for (i = 0; i < remote->fetch_refspec_nr; i++) {
-                               get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
-                               if (remote->fetch[i].dst &&
-                                   remote->fetch[i].dst[0])
+                       for (i = 0; i < remote->fetch.nr; i++) {
+                               get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0);
+                               if (remote->fetch.items[i].dst &&
+                                   remote->fetch.items[i].dst[0])
                                        *autotags = 1;
                                if (!i && !has_merge && ref_map &&
-                                   !remote->fetch[0].pattern)
+                                   !remote->fetch.items[0].pattern)
                                        ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        }
                        /*
@@ -457,7 +434,7 @@ static struct ref *get_ref_map(struct transport *transport,
                /* also fetch all tags */
                get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        else if (tags == TAGS_DEFAULT && *autotags)
-               find_non_local_tags(transport, &ref_map, &tail);
+               find_non_local_tags(remote_refs, &ref_map, &tail);
 
        /* Now append any refs to be updated opportunistically: */
        *tail = orefs;
@@ -466,7 +443,23 @@ static struct ref *get_ref_map(struct transport *transport,
                tail = &rm->next;
        }
 
-       return ref_remove_duplicates(ref_map);
+       ref_map = ref_remove_duplicates(ref_map);
+
+       for_each_ref(add_existing, &existing_refs);
+       for (rm = ref_map; rm; rm = rm->next) {
+               if (rm->peer_ref) {
+                       struct string_list_item *peer_item =
+                               string_list_lookup(&existing_refs,
+                                                  rm->peer_ref->name);
+                       if (peer_item) {
+                               struct object_id *old_oid = peer_item->util;
+                               oidcpy(&rm->peer_ref->old_oid, old_oid);
+                       }
+               }
+       }
+       string_list_clear(&existing_refs, 1);
+
+       return ref_map;
 }
 
 #define STORE_REF_ERROR_OTHER 1
@@ -776,7 +769,7 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
 }
 
 static int store_updated_refs(const char *raw_url, const char *remote_name,
-               struct ref *ref_map)
+                             int connectivity_checked, struct ref *ref_map)
 {
        FILE *fp;
        struct commit *commit;
@@ -798,10 +791,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        else
                url = xstrdup("foreign");
 
-       rm = ref_map;
-       if (check_connected(iterate_ref_map, &rm, NULL)) {
-               rc = error(_("%s did not send all necessary objects\n"), url);
-               goto abort;
+       if (!connectivity_checked) {
+               rm = ref_map;
+               if (check_connected(iterate_ref_map, &rm, NULL)) {
+                       rc = error(_("%s did not send all necessary objects\n"), url);
+                       goto abort;
+               }
        }
 
        prepare_format_display(ref_map);
@@ -953,24 +948,41 @@ static int quickfetch(struct ref *ref_map)
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_refs(struct transport *transport, struct ref *ref_map,
+                     struct ref **updated_remote_refs)
 {
        int ret = quickfetch(ref_map);
        if (ret)
-               ret = transport_fetch_refs(transport, ref_map);
+               ret = transport_fetch_refs(transport, ref_map,
+                                          updated_remote_refs);
        if (!ret)
-               ret |= store_updated_refs(transport->url,
-                               transport->remote->name,
-                               ref_map);
+               /*
+                * Keep the new pack's ".keep" file around to allow the caller
+                * time to update refs to reference the new objects.
+                */
+               return 0;
+       transport_unlock_pack(transport);
+       return ret;
+}
+
+/* Update local refs based on the ref values fetched from a remote */
+static int consume_refs(struct transport *transport, struct ref *ref_map)
+{
+       int connectivity_checked = transport->smart_options
+               ? transport->smart_options->connectivity_checked : 0;
+       int ret = store_updated_refs(transport->url,
+                                    transport->remote->name,
+                                    connectivity_checked,
+                                    ref_map);
        transport_unlock_pack(transport);
        return ret;
 }
 
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
-               const char *raw_url)
+static int prune_refs(struct refspec *rs, struct ref *ref_map,
+                     const char *raw_url)
 {
        int url_len, i, result = 0;
-       struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+       struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
        char *url;
        int summary_width = transport_summary_width(stale_refs);
        const char *dangling_msg = dry_run
@@ -1107,7 +1119,8 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       fetch_refs(transport, ref_map);
+       if (!fetch_refs(transport, ref_map, NULL))
+               consume_refs(transport, ref_map);
 
        if (gsecondary) {
                transport_disconnect(gsecondary);
@@ -1116,15 +1129,14 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
 }
 
 static int do_fetch(struct transport *transport,
-                   struct refspec *refs, int ref_count)
+                   struct refspec *rs)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct ref *ref_map;
-       struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
-
-       for_each_ref(add_existing, &existing_refs);
+       const struct ref *remote_refs;
+       struct ref *updated_remote_refs = NULL;
+       struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1140,22 +1152,24 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
-       ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
-       if (!update_head_ok)
-               check_not_current_branch(ref_map);
+       if (rs->nr)
+               refspec_ref_prefixes(rs, &ref_prefixes);
+       else if (transport->remote && transport->remote->fetch.nr)
+               refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
 
-       for (rm = ref_map; rm; rm = rm->next) {
-               if (rm->peer_ref) {
-                       struct string_list_item *peer_item =
-                               string_list_lookup(&existing_refs,
-                                                  rm->peer_ref->name);
-                       if (peer_item) {
-                               struct object_id *old_oid = peer_item->util;
-                               oidcpy(&rm->peer_ref->old_oid, old_oid);
-                       }
-               }
+       if (ref_prefixes.argc &&
+           (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
+               argv_array_push(&ref_prefixes, "refs/tags/");
        }
 
+       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       argv_array_clear(&ref_prefixes);
+
+       ref_map = get_ref_map(transport->remote, remote_refs, rs,
+                             tags, &autotags);
+       if (!update_head_ok)
+               check_not_current_branch(ref_map);
+
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (prune) {
@@ -1164,16 +1178,32 @@ static int do_fetch(struct transport *transport,
                 * explicitly (via command line or configuration); we
                 * don't care whether --tags was specified.
                 */
-               if (ref_count) {
-                       prune_refs(refs, ref_count, ref_map, transport->url);
+               if (rs->nr) {
+                       prune_refs(rs, ref_map, transport->url);
                } else {
-                       prune_refs(transport->remote->fetch,
-                                  transport->remote->fetch_refspec_nr,
+                       prune_refs(&transport->remote->fetch,
                                   ref_map,
                                   transport->url);
                }
        }
-       if (fetch_refs(transport, ref_map)) {
+
+       if (fetch_refs(transport, ref_map, &updated_remote_refs)) {
+               free_refs(ref_map);
+               retcode = 1;
+               goto cleanup;
+       }
+       if (updated_remote_refs) {
+               /*
+                * Regenerate ref_map using the updated remote refs.  This is
+                * to account for additional information which may be provided
+                * by the transport (e.g. shallow info).
+                */
+               free_refs(ref_map);
+               ref_map = get_ref_map(transport->remote, updated_remote_refs, rs,
+                                     tags, &autotags);
+               free_refs(updated_remote_refs);
+       }
+       if (consume_refs(transport, ref_map)) {
                free_refs(ref_map);
                retcode = 1;
                goto cleanup;
@@ -1185,14 +1215,13 @@ static int do_fetch(struct transport *transport,
        if (tags == TAGS_DEFAULT && autotags) {
                struct ref **tail = &ref_map;
                ref_map = NULL;
-               find_non_local_tags(transport, &ref_map, &tail);
+               find_non_local_tags(remote_refs, &ref_map, &tail);
                if (ref_map)
                        backfill_tags(transport, ref_map);
                free_refs(ref_map);
        }
 
  cleanup:
-       string_list_clear(&existing_refs, 1);
        return retcode;
 }
 
@@ -1357,10 +1386,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
 
 static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
 {
-       static const char **refs = NULL;
-       struct refspec *refspec;
-       int ref_nr = 0;
-       int j = 0;
+       struct refspec rs = REFSPEC_INIT_FETCH;
+       int i;
        int exit_code;
        int maybe_prune_tags;
        int remote_via_config = remote_is_configured(remote, 0);
@@ -1393,29 +1420,24 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru
 
        maybe_prune_tags = prune_tags_ok && prune_tags;
        if (maybe_prune_tags && remote_via_config)
-               add_prune_tags_to_fetch_refspec(remote);
-
-       if (argc > 0 || (maybe_prune_tags && !remote_via_config)) {
-               size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);
-               refs = xcalloc(nr_alloc, sizeof(const char *));
-               if (maybe_prune_tags) {
-                       refs[j++] = xstrdup("refs/tags/*:refs/tags/*");
-                       ref_nr++;
-               }
-       }
-
-       if (argc > 0) {
-               int i;
-               for (i = 0; i < argc; i++) {
-                       if (!strcmp(argv[i], "tag")) {
-                               i++;
-                               if (i >= argc)
-                                       die(_("You need to specify a tag name."));
-                               refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s",
-                                                   argv[i], argv[i]);
-                       } else
-                               refs[j++] = argv[i];
-                       ref_nr++;
+               refspec_append(&remote->fetch, TAG_REFSPEC);
+
+       if (maybe_prune_tags && (argc || !remote_via_config))
+               refspec_append(&rs, TAG_REFSPEC);
+
+       for (i = 0; i < argc; i++) {
+               if (!strcmp(argv[i], "tag")) {
+                       char *tag;
+                       i++;
+                       if (i >= argc)
+                               die(_("You need to specify a tag name."));
+
+                       tag = xstrfmt("refs/tags/%s:refs/tags/%s",
+                                     argv[i], argv[i]);
+                       refspec_append(&rs, tag);
+                       free(tag);
+               } else {
+                       refspec_append(&rs, argv[i]);
                }
        }
 
@@ -1424,9 +1446,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru
 
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
-       refspec = parse_fetch_refspec(ref_nr, refs);
-       exit_code = do_fetch(gtransport, refspec, ref_nr);
-       free_refspec(ref_nr, refspec);
+       exit_code = do_fetch(gtransport, &rs);
+       refspec_clear(&rs);
        transport_disconnect(gtransport);
        gtransport = NULL;
        return exit_code;