]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/fetch.c
Merge branch 'sb/object-store-lookup'
[thirdparty/git.git] / builtin / fetch.c
index ea5b9669ad1f40da56a8565cada56e5991d24206..c3c58a545349e33d73aa9f65c60f414a06c06ee4 100644 (file)
@@ -6,6 +6,7 @@
 #include "repository.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "commit.h"
 #include "builtin.h"
 #include "string-list.h"
@@ -93,19 +94,6 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
        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)
 {
        /*
@@ -254,9 +242,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;
@@ -264,7 +252,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;
 
@@ -338,7 +326,8 @@ static void find_non_local_tags(struct transport *transport,
        string_list_clear(&remote_refs, 0);
 }
 
-static struct ref *get_ref_map(struct transport *transport,
+static struct ref *get_ref_map(struct remote *remote,
+                              const struct ref *remote_refs,
                               struct refspec *rs,
                               int tags, int *autotags)
 {
@@ -346,26 +335,11 @@ static struct ref *get_ref_map(struct transport *transport,
        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;
-
-       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);
-
-       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);
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
 
        if (rs->nr) {
                struct refspec *fetch_refspec;
@@ -402,7 +376,7 @@ static struct ref *get_ref_map(struct transport *transport,
                if (refmap.nr)
                        fetch_refspec = &refmap;
                else
-                       fetch_refspec = &transport->remote->fetch;
+                       fetch_refspec = &remote->fetch;
 
                for (i = 0; i < fetch_refspec->nr; i++)
                        get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
@@ -410,7 +384,6 @@ static struct ref *get_ref_map(struct transport *transport,
                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 &&
@@ -449,7 +422,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;
@@ -458,7 +431,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
@@ -683,8 +672,10 @@ static int update_local_ref(struct ref *ref,
                return r;
        }
 
-       current = lookup_commit_reference_gently(&ref->old_oid, 1);
-       updated = lookup_commit_reference_gently(&ref->new_oid, 1);
+       current = lookup_commit_reference_gently(the_repository,
+                                                &ref->old_oid, 1);
+       updated = lookup_commit_reference_gently(the_repository,
+                                                &ref->new_oid, 1);
        if (!current || !updated) {
                const char *msg;
                const char *what;
@@ -768,7 +759,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;
@@ -777,7 +768,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        const char *what, *kind;
        struct ref *rm;
        char *url;
-       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
+       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
        int want_status;
        int summary_width = transport_summary_width(ref_map);
 
@@ -790,10 +781,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);
@@ -817,7 +810,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                continue;
                        }
 
-                       commit = lookup_commit_reference_gently(&rm->old_oid,
+                       commit = lookup_commit_reference_gently(the_repository,
+                                                               &rm->old_oid,
                                                                1);
                        if (!commit)
                                rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -945,15 +939,32 @@ 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;
 }
@@ -1029,7 +1040,7 @@ static void check_not_current_branch(struct ref *ref_map)
 
 static int truncate_fetch_head(void)
 {
-       const char *filename = git_path_fetch_head();
+       const char *filename = git_path_fetch_head(the_repository);
        FILE *fp = fopen_for_writing(filename);
 
        if (!fp)
@@ -1099,7 +1110,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);
@@ -1110,13 +1122,12 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
 static int do_fetch(struct transport *transport,
                    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)
@@ -1132,22 +1143,24 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
-       ref_map = get_ref_map(transport, rs, 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,7 +1177,24 @@ static int do_fetch(struct transport *transport,
                                   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;
@@ -1176,14 +1206,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;
 }
 
@@ -1433,7 +1462,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++)
                strbuf_addf(&default_rla, " %s", argv[i]);
 
-       config_from_gitmodules(gitmodules_fetch_config, NULL);
+       fetch_config_from_gitmodules(&max_children, &recurse_submodules);
        git_config(git_fetch_config, NULL);
 
        argc = parse_options(argc, argv, prefix,
@@ -1449,7 +1478,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (unshallow) {
                if (depth)
                        die(_("--depth and --unshallow cannot be used together"));
-               else if (!is_repository_shallow())
+               else if (!is_repository_shallow(the_repository))
                        die(_("--unshallow on a complete repository does not make sense"));
                else
                        depth = xstrfmt("%d", INFINITE_DEPTH);