Merge branch 'js/trace2-signo-typofix'
[gitweb.git] / builtin / fetch.c
index 0bf8fa7030fec46984503f80c9046b73f57fe648..53ce99d2bbc4efd0a37ad45feaad546ae2db7e13 100644 (file)
@@ -24,6 +24,8 @@
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
 
+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
+
 static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
        N_("git fetch [<options>] <group>"),
@@ -39,6 +41,8 @@ enum {
 };
 
 static int fetch_prune_config = -1; /* unspecified */
+static int fetch_show_forced_updates = 1;
+static uint64_t forced_updates_ms = 0;
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
@@ -48,6 +52,7 @@ static int prune_tags = -1; /* unspecified */
 
 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 static int progress = -1;
+static int enable_auto_gc = 1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
 static int max_children = 1;
 static enum transport_family family;
@@ -79,6 +84,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
                return 0;
        }
 
+       if (!strcmp(k, "fetch.showforcedupdates")) {
+               fetch_show_forced_updates = git_config_bool(k, v);
+               return 0;
+       }
+
        if (!strcmp(k, "submodule.recurse")) {
                int r = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@ -98,6 +108,8 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
 
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
 {
+       BUG_ON_OPT_NEG(unset);
+
        /*
         * "git fetch --refmap='' origin foo"
         * can be used to tell the command not to store anywhere
@@ -167,6 +179,10 @@ static struct option builtin_fetch_options[] = {
        OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
                        N_("report that we have only objects reachable from this object")),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+       OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+                N_("run 'gc --auto' after fetching")),
+       OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
+                N_("check for forced-updates on all updated branches")),
        OPT_END()
 };
 
@@ -237,6 +253,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
 struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
+       int ignore;
        char refname[FLEX_ARRAY];
 };
 
@@ -287,7 +304,7 @@ static int refname_hash_exists(struct hashmap *map, const char *refname)
 
 static void clear_item(struct refname_hash_entry *item)
 {
-       oidclr(&item->oid);
+       item->ignore = 1;
 }
 
 static void find_non_local_tags(const struct ref *refs,
@@ -320,8 +337,7 @@ static void find_non_local_tags(const struct ref *refs,
                            !has_object_file_with_flags(&ref->old_oid,
                                                        OBJECT_INFO_QUICK) &&
                            !will_fetch(head, ref->old_oid.hash) &&
-                           !has_sha1_file_with_flags(item->oid.hash,
-                                                     OBJECT_INFO_QUICK) &&
+                           !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->oid.hash))
                                clear_item(item);
                        item = NULL;
@@ -335,7 +351,7 @@ static void find_non_local_tags(const struct ref *refs,
                 * fetch.
                 */
                if (item &&
-                   !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) &&
+                   !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->oid.hash))
                        clear_item(item);
 
@@ -356,7 +372,7 @@ static void find_non_local_tags(const struct ref *refs,
         * checked to see if it needs fetching.
         */
        if (item &&
-           !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) &&
+           !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->oid.hash))
                clear_item(item);
 
@@ -373,7 +389,7 @@ static void find_non_local_tags(const struct ref *refs,
                        BUG("unseen remote ref?");
 
                /* Unless we have already decided to ignore this item... */
-               if (is_null_oid(&item->oid))
+               if (item->ignore)
                        continue;
 
                rm = alloc_ref(item->refname);
@@ -634,9 +650,14 @@ static int find_and_replace(struct strbuf *haystack,
                            const char *needle,
                            const char *placeholder)
 {
-       const char *p = strstr(haystack->buf, needle);
+       const char *p = NULL;
        int plen, nlen;
 
+       nlen = strlen(needle);
+       if (ends_with(haystack->buf, needle))
+               p = haystack->buf + haystack->len - nlen;
+       else
+               p = strstr(haystack->buf, needle);
        if (!p)
                return 0;
 
@@ -644,7 +665,6 @@ static int find_and_replace(struct strbuf *haystack,
                return 0;
 
        plen = strlen(p);
-       nlen = strlen(needle);
        if (plen > nlen && p[nlen] != '/')
                return 0;
 
@@ -701,6 +721,7 @@ static int update_local_ref(struct ref *ref,
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
        const char *pretty_ref = prettify_refname(ref->name);
+       int fast_forward = 0;
 
        type = oid_object_info(the_repository, &ref->new_oid, NULL);
        if (type < 0)
@@ -768,9 +789,6 @@ static int update_local_ref(struct ref *ref,
                        what = _("[new ref]");
                }
 
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref(msg, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
@@ -778,15 +796,21 @@ static int update_local_ref(struct ref *ref,
                return r;
        }
 
-       if (in_merge_bases(current, updated)) {
+       if (fetch_show_forced_updates) {
+               uint64_t t_before = getnanotime();
+               fast_forward = in_merge_bases(current, updated);
+               forced_updates_ms += (getnanotime() - t_before) / 1000000;
+       } else {
+               fast_forward = 1;
+       }
+
+       if (fast_forward) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
+
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("fast-forward", ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
@@ -799,9 +823,6 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "...");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                   (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("forced-update", ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced update"),
@@ -897,6 +918,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                ref->force = rm->peer_ref->force;
                        }
 
+                       if (recurse_submodules != RECURSE_SUBMODULES_OFF)
+                               check_for_new_submodule_commits(&rm->old_oid);
 
                        if (!strcmp(rm->name, "HEAD")) {
                                kind = "";
@@ -980,6 +1003,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches"), remote_name);
 
+       if (advice_fetch_show_forced_updates) {
+               if (!fetch_show_forced_updates) {
+                       warning(_("Fetch normally indicates which branches had a forced update, but that check has been disabled."));
+                       warning(_("To re-enable, use '--show-forced-updates' flag or run 'git config fetch.showForcedUpdates true'."));
+               } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
+                       warning(_("It took %.2f seconds to check forced updates. You can use '--no-show-forced-updates'\n"),
+                               forced_updates_ms / 1000.0);
+                       warning(_("or run 'git config fetch.showForcedUpdates false' to avoid this check.\n"));
+               }
+       }
+
  abort:
        strbuf_release(&note);
        free(url);
@@ -992,10 +1026,11 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
  * everything we are going to fetch already exists and is connected
  * locally.
  */
-static int quickfetch(struct ref *ref_map)
+static int check_exist_and_connected(struct ref *ref_map)
 {
        struct ref *rm = ref_map;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
+       struct ref *r;
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -1006,13 +1041,23 @@ static int quickfetch(struct ref *ref_map)
         */
        if (deepen)
                return -1;
+
+       /*
+        * check_connected() allows objects to merely be promised, but
+        * we need all direct targets to exist.
+        */
+       for (r = rm; r; r = r->next) {
+               if (!has_object_file(&r->old_oid))
+                       return -1;
+       }
+
        opt.quiet = 1;
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
 {
-       int ret = quickfetch(ref_map);
+       int ret = check_exist_and_connected(ref_map);
        if (ret)
                ret = transport_fetch_refs(transport, ref_map);
        if (!ret)
@@ -1166,6 +1211,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
 static struct transport *prepare_transport(struct remote *remote, int deepen)
 {
        struct transport *transport;
+
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
        transport->family = family;
@@ -1185,9 +1231,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        if (filter_options.choice) {
+               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               expand_list_objects_filter_spec(&filter_options,
+                                               &expanded_filter_spec);
                set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                          filter_options.filter_spec);
+                          expanded_filter_spec.buf);
                set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+               strbuf_release(&expanded_filter_spec);
        }
        if (negotiation_tip.nr) {
                if (transport->smart_options)
@@ -1236,6 +1286,7 @@ static int do_fetch(struct transport *transport,
        int retcode = 0;
        const struct ref *remote_refs;
        struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+       int must_list_refs = 1;
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1251,17 +1302,36 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
-       if (rs->nr)
+       if (rs->nr) {
+               int i;
+
                refspec_ref_prefixes(rs, &ref_prefixes);
-       else if (transport->remote && transport->remote->fetch.nr)
+
+               /*
+                * We can avoid listing refs if all of them are exact
+                * OIDs
+                */
+               must_list_refs = 0;
+               for (i = 0; i < rs->nr; i++) {
+                       if (!rs->items[i].exact_sha1) {
+                               must_list_refs = 1;
+                               break;
+                       }
+               }
+       } 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))) {
-               argv_array_push(&ref_prefixes, "refs/tags/");
+       if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
+               must_list_refs = 1;
+               if (ref_prefixes.argc)
+                       argv_array_push(&ref_prefixes, "refs/tags/");
        }
 
-       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       if (must_list_refs)
+               remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       else
+               remote_refs = NULL;
+
        argv_array_clear(&ref_prefixes);
 
        ref_map = get_ref_map(transport->remote, remote_refs, rs,
@@ -1397,7 +1467,7 @@ static int fetch_multiple(struct string_list *list)
                        return errcode;
        }
 
-       argv_array_pushl(&argv, "fetch", "--append", NULL);
+       argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
        add_options_to_argv(&argv);
 
        for (i = 0; i < list->nr; i++) {
@@ -1452,7 +1522,8 @@ static inline void fetch_one_setup_partial(struct remote *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"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialClone"));
                return;
        }
 
@@ -1528,7 +1599,9 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru
 
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
+       sigchain_push(SIGPIPE, SIG_IGN);
        exit_code = do_fetch(gtransport, &rs);
+       sigchain_pop(SIGPIPE);
        refspec_clear(&rs);
        transport_disconnect(gtransport);
        gtransport = NULL;
@@ -1620,7 +1693,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
                if (filter_options.choice)
-                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+                       die(_("--filter can only be used with the remote "
+                             "configured in extensions.partialclone"));
                /* TODO should this also die if we have a previous partial-clone? */
                result = fetch_multiple(&list);
        }
@@ -1641,13 +1715,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        string_list_clear(&list, 0);
 
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
 
-       argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
-       if (verbosity < 0)
-               argv_array_push(&argv_gc_auto, "--quiet");
-       run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
-       argv_array_clear(&argv_gc_auto);
+       if (enable_auto_gc) {
+               argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+               if (verbosity < 0)
+                       argv_array_push(&argv_gc_auto, "--quiet");
+               run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+               argv_array_clear(&argv_gc_auto);
+       }
 
        return result;
 }