fetch --tags: fetch tags *in addition to* other stuff
[gitweb.git] / builtin / fetch.c
index 61e8117c4a320c62a0cfa9e64378ccc6be0960ce..887fa3e5810356824e3b16d6bd48b67c051b1eff 100644 (file)
@@ -269,35 +269,47 @@ static struct ref *get_ref_map(struct transport *transport,
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
 
-       const struct ref *remote_refs = transport_get_remote_refs(transport);
+       /* opportunistically-updated references: */
+       struct ref *orefs = NULL, **oref_tail = &orefs;
 
-       if (refspec_count || tags == TAGS_SET) {
-               struct ref **old_tail;
+       const struct ref *remote_refs = transport_get_remote_refs(transport);
 
+       if (refspec_count) {
                for (i = 0; i < refspec_count; i++) {
                        get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
                        if (refspecs[i].dst && refspecs[i].dst[0])
                                *autotags = 1;
                }
-               /* Merge everything on the command line, but not --tags */
+               /* Merge everything on the command line (but not --tags) */
                for (rm = ref_map; rm; rm = rm->next)
                        rm->fetch_head_status = FETCH_HEAD_MERGE;
-               if (tags == TAGS_SET)
-                       get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 
                /*
-                * For any refs that we happen to be fetching via command-line
-                * arguments, take the opportunity to update their configured
-                * counterparts. However, we do not want to mention these
-                * entries in FETCH_HEAD at all, as they would simply be
-                * duplicates of existing entries.
+                * For any refs that we happen to be fetching via
+                * command-line arguments, the destination ref might
+                * have been missing or have been different than the
+                * remote-tracking ref that would be derived from the
+                * configured refspec.  In these cases, we want to
+                * take the opportunity to update their configured
+                * remote-tracking reference.  However, we do not want
+                * to mention these entries in FETCH_HEAD at all, as
+                * they would simply be duplicates of existing
+                * entries, so we set them FETCH_HEAD_IGNORE below.
+                *
+                * We compute these entries now, based only on the
+                * refspecs specified on the command line.  But we add
+                * them to the list following the refspecs resulting
+                * from the tags option so that one of the latter,
+                * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
+                * by ref_remove_duplicates() in favor of one of these
+                * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
-               old_tail = tail;
                for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
                        get_fetch_map(ref_map, &transport->remote->fetch[i],
-                                     &tail, 1);
-               for (rm = *old_tail; rm; rm = rm->next)
-                       rm->fetch_head_status = FETCH_HEAD_IGNORE;
+                                     &oref_tail, 1);
+
+               if (tags == TAGS_SET)
+                       get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@ -334,8 +346,20 @@ static struct ref *get_ref_map(struct transport *transport,
                        tail = &ref_map->next;
                }
        }
-       if (tags == TAGS_DEFAULT && *autotags)
+
+       if (tags == TAGS_SET)
+               /* 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);
+
+       /* Now append any refs to be updated opportunistically: */
+       *tail = orefs;
+       for (rm = orefs; rm; rm = rm->next) {
+               rm->fetch_head_status = FETCH_HEAD_IGNORE;
+               tail = &rm->next;
+       }
+
        ref_remove_duplicates(ref_map);
 
        return ref_map;
@@ -826,31 +850,38 @@ static int do_fetch(struct transport *transport,
                goto cleanup;
        }
        if (prune) {
-               /*
-                * If --tags was specified, pretend that the user gave us
-                * the canonical tags refspec
-                */
+               struct refspec *prune_refspecs;
+               int prune_refspec_count;
+
+               if (ref_count) {
+                       prune_refspecs = refs;
+                       prune_refspec_count = ref_count;
+               } else {
+                       prune_refspecs = transport->remote->fetch;
+                       prune_refspec_count = transport->remote->fetch_refspec_nr;
+               }
+
                if (tags == TAGS_SET) {
+                       /*
+                        * --tags was specified.  Pretend that the user also
+                        * gave us the canonical tags refspec
+                        */
                        const char *tags_str = "refs/tags/*:refs/tags/*";
                        struct refspec *tags_refspec, *refspec;
 
                        /* Copy the refspec and add the tags to it */
-                       refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
+                       refspec = xcalloc(prune_refspec_count + 1, sizeof(*refspec));
                        tags_refspec = parse_fetch_refspec(1, &tags_str);
-                       memcpy(refspec, refs, ref_count * sizeof(struct refspec));
-                       memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
-                       ref_count++;
+                       memcpy(refspec, prune_refspecs, prune_refspec_count * sizeof(*refspec));
+                       memcpy(&refspec[prune_refspec_count], tags_refspec, sizeof(*refspec));
 
-                       prune_refs(refspec, ref_count, ref_map);
+                       prune_refs(refspec, prune_refspec_count + 1, ref_map);
 
-                       ref_count--;
                        /* The rest of the strings belong to fetch_one */
                        free_refspec(1, tags_refspec);
                        free(refspec);
-               } else if (ref_count) {
-                       prune_refs(refs, ref_count, ref_map);
                } else {
-                       prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+                       prune_refs(prune_refspecs, prune_refspec_count, ref_map);
                }
        }
        free_refs(ref_map);