assert NOARG/NONEG behavior of parse-options callbacks
[gitweb.git] / builtin / fetch.c
index 0696abfc2a158baa3177fa9e71e2c16bc64655a4..354ed8bdef3a6511ddb580d97b75df60b77ac6ca 100644 (file)
@@ -98,6 +98,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
@@ -931,10 +933,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
@@ -945,13 +948,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)
@@ -1175,6 +1188,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)
@@ -1190,17 +1204,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,