Merge branch 'ms/fetch-prune-configuration'
[gitweb.git] / builtin / fetch.c
index 08ab9480223dca2dc8999b6b9755058ffb0254bb..593653955201995227c062d3878aafc09b424940 100644 (file)
@@ -69,15 +69,15 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
 
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
-       OPT_BOOLEAN(0, "all", &all,
-                   N_("fetch from all remotes")),
-       OPT_BOOLEAN('a', "append", &append,
-                   N_("append to .git/FETCH_HEAD instead of overwriting")),
+       OPT_BOOL(0, "all", &all,
+                N_("fetch from all remotes")),
+       OPT_BOOL('a', "append", &append,
+                N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
                   N_("path to upload pack on remote end")),
        OPT__FORCE(&force, N_("force overwrite of local branch")),
-       OPT_BOOLEAN('m', "multiple", &multiple,
-                   N_("fetch from multiple remotes")),
+       OPT_BOOL('m', "multiple", &multiple,
+                N_("fetch from multiple remotes")),
        OPT_SET_INT('t', "tags", &tags,
                    N_("fetch all tags and associated objects"), TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
@@ -87,10 +87,10 @@ static struct option builtin_fetch_options[] = {
        { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
                    N_("control recursive fetching of submodules"),
                    PARSE_OPT_OPTARG, option_parse_recurse_submodules },
-       OPT_BOOLEAN(0, "dry-run", &dry_run,
-                   N_("dry run")),
-       OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")),
-       OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
+       OPT_BOOL(0, "dry-run", &dry_run,
+                N_("dry run")),
+       OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+       OPT_BOOL('u', "update-head-ok", &update_head_ok,
                    N_("allow updating of HEAD ref")),
        OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
        OPT_STRING(0, "depth", &depth, N_("depth"),
@@ -132,7 +132,7 @@ static void add_merge_config(struct ref **head,
 
                for (rm = *head; rm; rm = rm->next) {
                        if (branch_merge_matches(branch, i, rm->name)) {
-                               rm->merge = 1;
+                               rm->fetch_head_status = FETCH_HEAD_MERGE;
                                break;
                        }
                }
@@ -153,7 +153,7 @@ static void add_merge_config(struct ref **head,
                refspec.src = branch->merge[i]->src;
                get_fetch_map(remote_refs, &refspec, tail, 1);
                for (rm = *old_tail; rm; rm = rm->next)
-                       rm->merge = 1;
+                       rm->fetch_head_status = FETCH_HEAD_MERGE;
        }
 }
 
@@ -173,6 +173,8 @@ static struct ref *get_ref_map(struct transport *transport,
        const struct ref *remote_refs = transport_get_remote_refs(transport);
 
        if (ref_count || tags == TAGS_SET) {
+               struct ref **old_tail;
+
                for (i = 0; i < ref_count; i++) {
                        get_fetch_map(remote_refs, &refs[i], &tail, 0);
                        if (refs[i].dst && refs[i].dst[0])
@@ -180,9 +182,23 @@ static struct ref *get_ref_map(struct transport *transport,
                }
                /* Merge everything on the command line, but not --tags */
                for (rm = ref_map; rm; rm = rm->next)
-                       rm->merge = 1;
+                       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.
+                */
+               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;
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@ -199,7 +215,7 @@ static struct ref *get_ref_map(struct transport *transport,
                                        *autotags = 1;
                                if (!i && !has_merge && ref_map &&
                                    !remote->fetch[0].pattern)
-                                       ref_map->merge = 1;
+                                       ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        }
                        /*
                         * if the remote we're fetching from is the same
@@ -215,7 +231,7 @@ static struct ref *get_ref_map(struct transport *transport,
                        ref_map = get_remote_ref(remote_refs, "HEAD");
                        if (!ref_map)
                                die(_("Couldn't find remote ref HEAD"));
-                       ref_map->merge = 1;
+                       ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        tail = &ref_map->next;
                }
        }
@@ -402,7 +418,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        const char *what, *kind;
        struct ref *rm;
        char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
-       int want_merge;
+       int want_status;
 
        fp = fopen(filename, "a");
        if (!fp)
@@ -420,19 +436,22 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        }
 
        /*
-        * The first pass writes objects to be merged and then the
-        * second pass writes the rest, in order to allow using
-        * FETCH_HEAD as a refname to refer to the ref to be merged.
+        * We do a pass for each fetch_head_status type in their enum order, so
+        * merged entries are written before not-for-merge. That lets readers
+        * use FETCH_HEAD as a refname to refer to the ref to be merged.
         */
-       for (want_merge = 1; 0 <= want_merge; want_merge--) {
+       for (want_status = FETCH_HEAD_MERGE;
+            want_status <= FETCH_HEAD_IGNORE;
+            want_status++) {
                for (rm = ref_map; rm; rm = rm->next) {
                        struct ref *ref = NULL;
+                       const char *merge_status_marker = "";
 
                        commit = lookup_commit_reference_gently(rm->old_sha1, 1);
                        if (!commit)
-                               rm->merge = 0;
+                               rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
 
-                       if (rm->merge != want_merge)
+                       if (rm->fetch_head_status != want_status)
                                continue;
 
                        if (rm->peer_ref) {
@@ -478,16 +497,26 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                        strbuf_addf(&note, "%s ", kind);
                                strbuf_addf(&note, "'%s' of ", what);
                        }
-                       fprintf(fp, "%s\t%s\t%s",
-                               sha1_to_hex(rm->old_sha1),
-                               rm->merge ? "" : "not-for-merge",
-                               note.buf);
-                       for (i = 0; i < url_len; ++i)
-                               if ('\n' == url[i])
-                                       fputs("\\n", fp);
-                               else
-                                       fputc(url[i], fp);
-                       fputc('\n', fp);
+                       switch (rm->fetch_head_status) {
+                       case FETCH_HEAD_NOT_FOR_MERGE:
+                               merge_status_marker = "not-for-merge";
+                               /* fall-through */
+                       case FETCH_HEAD_MERGE:
+                               fprintf(fp, "%s\t%s\t%s",
+                                       sha1_to_hex(rm->old_sha1),
+                                       merge_status_marker,
+                                       note.buf);
+                               for (i = 0; i < url_len; ++i)
+                                       if ('\n' == url[i])
+                                               fputs("\\n", fp);
+                                       else
+                                               fputc(url[i], fp);
+                               fputc('\n', fp);
+                               break;
+                       default:
+                               /* do not write anything to FETCH_HEAD */
+                               break;
+                       }
 
                        strbuf_reset(&note);
                        if (ref) {
@@ -584,7 +613,8 @@ static int add_existing(const char *refname, const unsigned char *sha1,
 {
        struct string_list *list = (struct string_list *)cbdata;
        struct string_list_item *item = string_list_insert(list, refname);
-       item->util = (void *)sha1;
+       item->util = xmalloc(20);
+       hashcpy(item->util, sha1);
        return 0;
 }
 
@@ -603,7 +633,7 @@ static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
@@ -649,7 +679,7 @@ static void find_non_local_tags(struct transport *transport,
                item = string_list_insert(&remote_refs, ref->name);
                item->util = (void *)ref->old_sha1;
        }
-       string_list_clear(&existing_refs, 0);
+       string_list_clear(&existing_refs, 1);
 
        /*
         * We may have a final lightweight tag that needs to be
@@ -706,11 +736,11 @@ static int truncate_fetch_head(void)
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
-       struct string_list_item *peer_item = NULL;
+       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);
 
@@ -726,9 +756,9 @@ static int do_fetch(struct transport *transport,
 
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
-               int errcode = truncate_fetch_head();
-               if (errcode)
-                       return errcode;
+               retcode = truncate_fetch_head();
+               if (retcode)
+                       goto cleanup;
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
@@ -737,8 +767,9 @@ static int do_fetch(struct transport *transport,
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
-                       peer_item = string_list_lookup(&existing_refs,
-                                                      rm->peer_ref->name);
+                       struct string_list_item *peer_item =
+                               string_list_lookup(&existing_refs,
+                                                  rm->peer_ref->name);
                        if (peer_item)
                                hashcpy(rm->peer_ref->old_sha1,
                                        peer_item->util);
@@ -749,7 +780,8 @@ static int do_fetch(struct transport *transport,
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (fetch_refs(transport, ref_map)) {
                free_refs(ref_map);
-               return 1;
+               retcode = 1;
+               goto cleanup;
        }
        if (prune) {
                /*
@@ -795,7 +827,9 @@ static int do_fetch(struct transport *transport,
                free_refs(ref_map);
        }
 
-       return 0;
+ cleanup:
+       string_list_clear(&existing_refs, 1);
+       return retcode;
 }
 
 static void set_option(const char *name, const char *value)