Merge branch 'ks/no-textconv-symlink'
[gitweb.git] / builtin / fetch.c
index 8654fa7a2dbe2c1ca76a493a4322447de5219b99..d35f000c034964ed600b1d8d45e2ef9260abcfb6 100644 (file)
 #include "run-command.h"
 #include "parse-options.h"
 #include "sigchain.h"
+#include "transport.h"
 
 static const char * const builtin_fetch_usage[] = {
-       "git fetch [options] [<repository> <refspec>...]",
-       "git fetch [options] <group>",
-       "git fetch --multiple [options] [<repository> | <group>]...",
-       "git fetch --all [options]",
+       "git fetch [<options>] [<repository> [<refspec>...]]",
+       "git fetch [<options>] <group>",
+       "git fetch --multiple [<options>] [(<repository> | <group>)...]",
+       "git fetch --all [<options>]",
        NULL
 };
 
@@ -27,6 +28,7 @@ enum {
 };
 
 static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
+static int progress;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -56,6 +58,7 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
+       OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
        OPT_STRING(0, "depth", &depth, "DEPTH",
                   "deepen history of shallow clone"),
        OPT_END()
@@ -104,10 +107,8 @@ static void add_merge_config(struct ref **head,
                 * there is no entry in the resulting FETCH_HEAD marked
                 * for merging.
                 */
+               memset(&refspec, 0, sizeof(refspec));
                refspec.src = branch->merge[i]->src;
-               refspec.dst = NULL;
-               refspec.pattern = 0;
-               refspec.force = 0;
                get_fetch_map(remote_refs, &refspec, tail, 1);
                for (rm = *old_tail; rm; rm = rm->next)
                        rm->merge = 1;
@@ -145,7 +146,10 @@ static struct ref *get_ref_map(struct transport *transport,
                struct remote *remote = transport->remote;
                struct branch *branch = branch_get(NULL);
                int has_merge = branch_has_merge_config(branch);
-               if (remote && (remote->fetch_refspec_nr || has_merge)) {
+               if (remote &&
+                   (remote->fetch_refspec_nr ||
+                    /* Note: has_merge implies non-NULL branch->remote_name */
+                    (has_merge && !strcmp(branch->remote_name, remote->name)))) {
                        for (i = 0; i < remote->fetch_refspec_nr; i++) {
                                get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);
                                if (remote->fetch[i].dst &&
@@ -159,6 +163,8 @@ static struct ref *get_ref_map(struct transport *transport,
                         * if the remote we're fetching from is the same
                         * as given in branch.<name>.remote, we add the
                         * ref given in branch.<name>.merge, too.
+                        *
+                        * Note: has_merge implies non-NULL branch->remote_name
                         */
                        if (has_merge &&
                            !strcmp(branch->remote_name, remote->name))
@@ -205,7 +211,6 @@ static int s_update_ref(const char *action,
        return 0;
 }
 
-#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 #define REFCOL_WIDTH  10
 
 static int update_local_ref(struct ref *ref,
@@ -224,7 +229,7 @@ static int update_local_ref(struct ref *ref,
 
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbosity > 0)
-                       sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
+                       sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
                                "[up to date]", REFCOL_WIDTH, remote,
                                pretty_ref);
                return 0;
@@ -239,7 +244,7 @@ static int update_local_ref(struct ref *ref,
                 * the head, and the old value of the head isn't empty...
                 */
                sprintf(display, "! %-*s %-*s -> %s  (can't fetch in current branch)",
-                       SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
        }
@@ -249,7 +254,7 @@ static int update_local_ref(struct ref *ref,
                int r;
                r = s_update_ref("updating tag", ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
-                       SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                return r;
        }
@@ -271,7 +276,7 @@ static int update_local_ref(struct ref *ref,
 
                r = s_update_ref(msg, ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
-                       SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
+                       TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
                        r ? "  (unable to update local ref)" : "");
                return r;
        }
@@ -284,7 +289,7 @@ static int update_local_ref(struct ref *ref,
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
                r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
-                       SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                return r;
        } else if (force || ref->force) {
@@ -295,13 +300,13 @@ static int update_local_ref(struct ref *ref,
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
                r = s_update_ref("forced-update", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
-                       SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref,
                        r ? "unable to update local ref" : "forced update");
                return r;
        } else {
                sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
-                       SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
+                       TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
        }
@@ -389,11 +394,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                fputc(url[i], fp);
                fputc('\n', fp);
 
-               if (ref)
+               if (ref) {
                        rc |= update_local_ref(ref, what, note);
-               else
+                       free(ref);
+               } else
                        sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
-                               SUMMARY_WIDTH, *kind ? kind : "branch",
+                               TRANSPORT_SUMMARY_WIDTH, *kind ? kind : "branch",
                                 REFCOL_WIDTH, *what ? what : "HEAD");
                if (*note) {
                        if (verbosity >= 0 && !shown_url) {
@@ -514,7 +520,7 @@ static int prune_refs(struct transport *transport, struct ref *ref_map)
                        result |= delete_ref(ref->name, NULL, 0);
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
-                               SUMMARY_WIDTH, "[deleted]",
+                               TRANSPORT_SUMMARY_WIDTH, "[deleted]",
                                REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
@@ -527,7 +533,7 @@ static int add_existing(const char *refname, const unsigned char *sha1,
                        int flag, void *cbdata)
 {
        struct string_list *list = (struct string_list *)cbdata;
-       struct string_list_item *item = string_list_insert(refname, list);
+       struct string_list_item *item = string_list_insert(list, refname);
        item->util = (void *)sha1;
        return 0;
 }
@@ -543,37 +549,12 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
-struct tag_data {
-       struct ref **head;
-       struct ref ***tail;
-};
-
-static int add_to_tail(struct string_list_item *item, void *cb_data)
-{
-       struct tag_data *data = (struct tag_data *)cb_data;
-       struct ref *rm = NULL;
-
-       /* We have already decided to ignore this item */
-       if (!item->util)
-               return 0;
-
-       rm = alloc_ref(item->string);
-       rm->peer_ref = alloc_ref(item->string);
-       hashcpy(rm->old_sha1, item->util);
-
-       **data->tail = rm;
-       *data->tail = &rm->next;
-
-       return 0;
-}
-
 static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
-       struct string_list remote_refs = { NULL, 0, 0, 0 };
-       struct tag_data data = {head, tail};
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
 
@@ -588,7 +569,7 @@ static void find_non_local_tags(struct transport *transport,
                 * to fetch then we can mark the ref entry in the list
                 * as one to ignore by setting util to NULL.
                 */
-               if (!strcmp(ref->name + strlen(ref->name) - 3, "^{}")) {
+               if (!suffixcmp(ref->name, "^{}")) {
                        if (item && !has_sha1_file(ref->old_sha1) &&
                            !will_fetch(head, ref->old_sha1) &&
                            !has_sha1_file(item->util) &&
@@ -615,7 +596,7 @@ static void find_non_local_tags(struct transport *transport,
                    string_list_has_string(&existing_refs, ref->name))
                        continue;
 
-               item = string_list_insert(ref->name, &remote_refs);
+               item = string_list_insert(&remote_refs, ref->name);
                item->util = (void *)ref->old_sha1;
        }
        string_list_clear(&existing_refs, 0);
@@ -629,10 +610,20 @@ static void find_non_local_tags(struct transport *transport,
                item->util = NULL;
 
        /*
-        * For all the tags in the remote_refs string list, call
-        * add_to_tail to add them to the list of refs to be fetched
+        * For all the tags in the remote_refs string list,
+        * add them to the list of refs to be fetched
         */
-       for_each_string_list(add_to_tail, &remote_refs, &data);
+       for_each_string_list_item(item, &remote_refs) {
+               /* Unless we have already decided to ignore this item... */
+               if (item->util)
+               {
+                       struct ref *rm = alloc_ref(item->string);
+                       rm->peer_ref = alloc_ref(item->string);
+                       hashcpy(rm->old_sha1, item->util);
+                       **tail = rm;
+                       *tail = &rm->next;
+               }
+       }
 
        string_list_clear(&remote_refs, 0);
 }
@@ -651,10 +642,21 @@ static void check_not_current_branch(struct ref *ref_map)
                            "of non-bare repository", current_branch->refname);
 }
 
+static int truncate_fetch_head(void)
+{
+       char *filename = git_path("FETCH_HEAD");
+       FILE *fp = fopen(filename, "w");
+
+       if (!fp)
+               return error("cannot open %s: %s\n", filename, strerror(errno));
+       fclose(fp);
+       return 0;
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
@@ -662,21 +664,21 @@ static int do_fetch(struct transport *transport,
 
        for_each_ref(add_existing, &existing_refs);
 
-       if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
-               tags = TAGS_SET;
-       if (transport->remote->fetch_tags == -1)
-               tags = TAGS_UNSET;
+       if (tags == TAGS_DEFAULT) {
+               if (transport->remote->fetch_tags == 2)
+                       tags = TAGS_SET;
+               if (transport->remote->fetch_tags == -1)
+                       tags = TAGS_UNSET;
+       }
 
        if (!transport->get_refs_list || !transport->fetch)
                die("Don't know how to fetch from %s", transport->url);
 
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
-               char *filename = git_path("FETCH_HEAD");
-               FILE *fp = fopen(filename, "w");
-               if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
-               fclose(fp);
+               int errcode = truncate_fetch_head();
+               if (errcode)
+                       return errcode;
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
@@ -685,8 +687,8 @@ static int do_fetch(struct transport *transport,
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
-                       peer_item = string_list_lookup(rm->peer_ref->name,
-                                                      &existing_refs);
+                       peer_item = string_list_lookup(&existing_refs,
+                                                      rm->peer_ref->name);
                        if (peer_item)
                                hashcpy(rm->peer_ref->old_sha1,
                                        peer_item->util);
@@ -735,7 +737,7 @@ static int get_one_remote_for_fetch(struct remote *remote, void *priv)
 {
        struct string_list *list = priv;
        if (!remote->skip_default_update)
-               string_list_append(remote->name, list);
+               string_list_append(list, remote->name);
        return 0;
 }
 
@@ -754,8 +756,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
                int space = strcspn(value, " \t\n");
                while (*value) {
                        if (space > 1) {
-                               string_list_append(xstrndup(value, space),
-                                                  g->list);
+                               string_list_append(g->list,
+                                                  xstrndup(value, space));
                        }
                        value += space + (value[space] != '\0');
                        space = strcspn(value, " \t\n");
@@ -768,7 +770,8 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 static int add_remote_or_group(const char *name, struct string_list *list)
 {
        int prev_nr = list->nr;
-       struct remote_group_data g = { name, list };
+       struct remote_group_data g;
+       g.name = name; g.list = list;
 
        git_config(get_remote_group, &g);
        if (list->nr == prev_nr) {
@@ -776,7 +779,7 @@ static int add_remote_or_group(const char *name, struct string_list *list)
                if (!remote_is_configured(name))
                        return 0;
                remote = remote_get(name);
-               string_list_append(remote->name, list);
+               string_list_append(list, remote->name);
        }
        return 1;
 }
@@ -784,13 +787,19 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
        int i, result = 0;
-       const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
-       int argc = 1;
+       const char *argv[11] = { "fetch", "--append" };
+       int argc = 2;
 
        if (dry_run)
                argv[argc++] = "--dry-run";
        if (prune)
                argv[argc++] = "--prune";
+       if (update_head_ok)
+               argv[argc++] = "--update-head-ok";
+       if (force)
+               argv[argc++] = "--force";
+       if (keep)
+               argv[argc++] = "--keep";
        if (verbosity >= 2)
                argv[argc++] = "-v";
        if (verbosity >= 1)
@@ -798,9 +807,16 @@ static int fetch_multiple(struct string_list *list)
        else if (verbosity < 0)
                argv[argc++] = "-q";
 
+       if (!append && !dry_run) {
+               int errcode = truncate_fetch_head();
+               if (errcode)
+                       return errcode;
+       }
+
        for (i = 0; i < list->nr; i++) {
                const char *name = list->items[i].string;
                argv[argc] = name;
+               argv[argc + 1] = NULL;
                if (verbosity >= 0)
                        printf("Fetching %s\n", name);
                if (run_command_v_opt(argv, RUN_GIT_CMD)) {
@@ -820,13 +836,11 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
        int exit_code;
 
        if (!remote)
-               die("Where do you want to fetch from today?");
+               die("No remote repository specified.  Please, specify either a URL or a\n"
+                   "remote name from which new revisions should be fetched.");
 
        transport = transport_get(remote, NULL);
-       if (verbosity >= 2)
-               transport->verbose = verbosity <= 3 ? verbosity : 3;
-       if (verbosity < 0)
-               transport->verbose = -1;
+       transport_set_verbosity(transport, verbosity, progress);
        if (upload_pack)
                set_option(TRANS_OPT_UPLOADPACK, upload_pack);
        if (keep)
@@ -868,7 +882,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
-       struct string_list list = { NULL, 0, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        struct remote *remote;
        int result = 0;