Merge branch 'jt/fetch-no-redundant-tag-fetch-map' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Jan 2017 23:19:09 +0000 (15:19 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Jan 2017 23:19:09 +0000 (15:19 -0800)
Code cleanup to avoid using redundant refspecs while fetching with
the --tags option.

* jt/fetch-no-redundant-tag-fetch-map:
fetch: do not redundantly calculate tag refmap

1  2 
builtin/fetch.c
diff --combined builtin/fetch.c
index b6a5597cbf332fc56b5feabfd29d03c98d4d6686,606364593ea272bf5c16715cc06ae0dc74de4d47..1d77e58a00af528d2d883690b30b9bf48df6d2aa
@@@ -15,7 -15,6 +15,7 @@@
  #include "submodule.h"
  #include "connected.h"
  #include "argv-array.h"
 +#include "utf8.h"
  
  static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@@ -35,15 -34,13 +35,15 @@@ static int fetch_prune_config = -1; /* 
  static int prune = -1; /* unspecified */
  #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
  
 -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
 +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 -static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 -static int max_children = 1;
 +static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
 +static int max_children = -1;
  static enum transport_family family;
  static const char *depth;
 +static const char *deepen_since;
  static const char *upload_pack;
 +static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
  static struct strbuf default_rla = STRBUF_INIT;
  static struct transport *gtransport;
  static struct transport *gsecondary;
@@@ -119,12 -116,6 +119,12 @@@ static struct option builtin_fetch_opti
        OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
        OPT_STRING(0, "depth", &depth, N_("depth"),
                   N_("deepen history of shallow clone")),
 +      OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
 +                 N_("deepen history of shallow repository based on time")),
 +      OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
 +                      N_("deepen history of shallow clone by excluding rev")),
 +      OPT_INTEGER(0, "deepen", &deepen_relative,
 +                  N_("deepen history of shallow clone")),
        { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
                   N_("convert to a complete repository"),
                   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
@@@ -241,10 -232,9 +241,10 @@@ static void find_non_local_tags(struct 
                 * as one to ignore by setting util to NULL.
                 */
                if (ends_with(ref->name, "^{}")) {
 -                      if (item && !has_object_file(&ref->old_oid) &&
 +                      if (item &&
 +                          !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
                            !will_fetch(head, ref->old_oid.hash) &&
 -                          !has_sha1_file(item->util) &&
 +                          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
                            !will_fetch(head, item->util))
                                item->util = NULL;
                        item = NULL;
                 * to check if it is a lightweight tag that we want to
                 * fetch.
                 */
 -              if (item && !has_sha1_file(item->util) &&
 +              if (item &&
 +                  !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
                    !will_fetch(head, item->util))
                        item->util = NULL;
  
         * We may have a final lightweight tag that needs to be
         * checked to see if it needs fetching.
         */
 -      if (item && !has_sha1_file(item->util) &&
 +      if (item &&
 +          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
            !will_fetch(head, item->util))
                item->util = NULL;
  
@@@ -359,9 -347,6 +359,6 @@@ static struct ref *get_ref_map(struct t
  
                for (i = 0; i < fetch_refspec_nr; i++)
                        get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
-               if (tags == TAGS_SET)
-                       get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        } else if (refmap_array) {
                die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
@@@ -461,141 -446,12 +458,141 @@@ fail
                           : STORE_REF_ERROR_OTHER;
  }
  
 -#define REFCOL_WIDTH  10
 +static int refcol_width = 10;
 +static int compact_format;
 +
 +static void adjust_refcol_width(const struct ref *ref)
 +{
 +      int max, rlen, llen, len;
 +
 +      /* uptodate lines are only shown on high verbosity level */
 +      if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))
 +              return;
 +
 +      max    = term_columns();
 +      rlen   = utf8_strwidth(prettify_refname(ref->name));
 +
 +      llen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));
 +
 +      /*
 +       * rough estimation to see if the output line is too long and
 +       * should not be counted (we can't do precise calculation
 +       * anyway because we don't know if the error explanation part
 +       * will be printed in update_local_ref)
 +       */
 +      if (compact_format) {
 +              llen = 0;
 +              max = max * 2 / 3;
 +      }
 +      len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
 +      if (len >= max)
 +              return;
 +
 +      /*
 +       * Not precise calculation for compact mode because '*' can
 +       * appear on the left hand side of '->' and shrink the column
 +       * back.
 +       */
 +      if (refcol_width < rlen)
 +              refcol_width = rlen;
 +}
 +
 +static void prepare_format_display(struct ref *ref_map)
 +{
 +      struct ref *rm;
 +      const char *format = "full";
 +
 +      git_config_get_string_const("fetch.output", &format);
 +      if (!strcasecmp(format, "full"))
 +              compact_format = 0;
 +      else if (!strcasecmp(format, "compact"))
 +              compact_format = 1;
 +      else
 +              die(_("configuration fetch.output contains invalid value %s"),
 +                  format);
 +
 +      for (rm = ref_map; rm; rm = rm->next) {
 +              if (rm->status == REF_STATUS_REJECT_SHALLOW ||
 +                  !rm->peer_ref ||
 +                  !strcmp(rm->name, "HEAD"))
 +                      continue;
 +
 +              adjust_refcol_width(rm);
 +      }
 +}
 +
 +static void print_remote_to_local(struct strbuf *display,
 +                                const char *remote, const char *local)
 +{
 +      strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
 +}
 +
 +static int find_and_replace(struct strbuf *haystack,
 +                          const char *needle,
 +                          const char *placeholder)
 +{
 +      const char *p = strstr(haystack->buf, needle);
 +      int plen, nlen;
 +
 +      if (!p)
 +              return 0;
 +
 +      if (p > haystack->buf && p[-1] != '/')
 +              return 0;
 +
 +      plen = strlen(p);
 +      nlen = strlen(needle);
 +      if (plen > nlen && p[nlen] != '/')
 +              return 0;
 +
 +      strbuf_splice(haystack, p - haystack->buf, nlen,
 +                    placeholder, strlen(placeholder));
 +      return 1;
 +}
 +
 +static void print_compact(struct strbuf *display,
 +                        const char *remote, const char *local)
 +{
 +      struct strbuf r = STRBUF_INIT;
 +      struct strbuf l = STRBUF_INIT;
 +
 +      if (!strcmp(remote, local)) {
 +              strbuf_addf(display, "%-*s -> *", refcol_width, remote);
 +              return;
 +      }
 +
 +      strbuf_addstr(&r, remote);
 +      strbuf_addstr(&l, local);
 +
 +      if (!find_and_replace(&r, local, "*"))
 +              find_and_replace(&l, remote, "*");
 +      print_remote_to_local(display, r.buf, l.buf);
 +
 +      strbuf_release(&r);
 +      strbuf_release(&l);
 +}
 +
 +static void format_display(struct strbuf *display, char code,
 +                         const char *summary, const char *error,
 +                         const char *remote, const char *local,
 +                         int summary_width)
 +{
 +      int width = (summary_width + strlen(summary) - gettext_width(summary));
 +
 +      strbuf_addf(display, "%c %-*s ", code, width, summary);
 +      if (!compact_format)
 +              print_remote_to_local(display, remote, local);
 +      else
 +              print_compact(display, remote, local);
 +      if (error)
 +              strbuf_addf(display, "  (%s)", error);
 +}
  
  static int update_local_ref(struct ref *ref,
                            const char *remote,
                            const struct ref *remote_ref,
 -                          struct strbuf *display)
 +                          struct strbuf *display,
 +                          int summary_width)
  {
        struct commit *current = NULL, *updated;
        enum object_type type;
  
        if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
                if (verbosity > 0)
 -                      strbuf_addf(display, "= %-*s %-*s -> %s",
 -                                  TRANSPORT_SUMMARY(_("[up to date]")),
 -                                  REFCOL_WIDTH, remote, pretty_ref);
 +                      format_display(display, '=', _("[up to date]"), NULL,
 +                                     remote, pretty_ref, summary_width);
                return 0;
        }
  
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
                 */
 -              strbuf_addf(display,
 -                          _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
 -                          TRANSPORT_SUMMARY(_("[rejected]")),
 -                          REFCOL_WIDTH, remote, pretty_ref);
 +              format_display(display, '!', _("[rejected]"),
 +                             _("can't fetch in current branch"),
 +                             remote, pretty_ref, summary_width);
                return 1;
        }
  
            starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
 -              strbuf_addf(display, "%c %-*s %-*s -> %s%s",
 -                          r ? '!' : '-',
 -                          TRANSPORT_SUMMARY(_("[tag update]")),
 -                          REFCOL_WIDTH, remote, pretty_ref,
 -                          r ? _("  (unable to update local ref)") : "");
 +              format_display(display, r ? '!' : 't', _("[tag update]"),
 +                             r ? _("unable to update local ref") : NULL,
 +                             remote, pretty_ref, summary_width);
                return r;
        }
  
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref(msg, ref, 0);
 -              strbuf_addf(display, "%c %-*s %-*s -> %s%s",
 -                          r ? '!' : '*',
 -                          TRANSPORT_SUMMARY(what),
 -                          REFCOL_WIDTH, remote, pretty_ref,
 -                          r ? _("  (unable to update local ref)") : "");
 +              format_display(display, r ? '!' : '*', what,
 +                             r ? _("unable to update local ref") : NULL,
 +                             remote, pretty_ref, summary_width);
                return r;
        }
  
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref("fast-forward", ref, 1);
 -              strbuf_addf(display, "%c %-*s %-*s -> %s%s",
 -                          r ? '!' : ' ',
 -                          TRANSPORT_SUMMARY_WIDTH, quickref.buf,
 -                          REFCOL_WIDTH, remote, pretty_ref,
 -                          r ? _("  (unable to update local ref)") : "");
 +              format_display(display, r ? '!' : ' ', quickref.buf,
 +                             r ? _("unable to update local ref") : NULL,
 +                             remote, pretty_ref, summary_width);
                strbuf_release(&quickref);
                return r;
        } else if (force || ref->force) {
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
                        check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref("forced-update", ref, 1);
 -              strbuf_addf(display, "%c %-*s %-*s -> %s  (%s)",
 -                          r ? '!' : '+',
 -                          TRANSPORT_SUMMARY_WIDTH, quickref.buf,
 -                          REFCOL_WIDTH, remote, pretty_ref,
 -                          r ? _("unable to update local ref") : _("forced update"));
 +              format_display(display, r ? '!' : '+', quickref.buf,
 +                             r ? _("unable to update local ref") : _("forced update"),
 +                             remote, pretty_ref, summary_width);
                strbuf_release(&quickref);
                return r;
        } else {
 -              strbuf_addf(display, "! %-*s %-*s -> %s  %s",
 -                          TRANSPORT_SUMMARY(_("[rejected]")),
 -                          REFCOL_WIDTH, remote, pretty_ref,
 -                          _("(non-fast-forward)"));
 +              format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
 +                             remote, pretty_ref, summary_width);
                return 1;
        }
  }
@@@ -733,11 -601,10 +730,11 @@@ static int store_updated_refs(const cha
        char *url;
        const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
        int want_status;
 +      int summary_width = transport_summary_width(ref_map);
  
        fp = fopen(filename, "a");
        if (!fp)
 -              return error(_("cannot open %s: %s\n"), filename, strerror(errno));
 +              return error_errno(_("cannot open %s"), filename);
  
        if (raw_url)
                url = transport_anonymize_url(raw_url);
                url = xstrdup("foreign");
  
        rm = ref_map;
 -      if (check_everything_connected(iterate_ref_map, 0, &rm)) {
 +      if (check_connected(iterate_ref_map, &rm, NULL)) {
                rc = error(_("%s did not send all necessary objects\n"), url);
                goto abort;
        }
  
 +      prepare_format_display(ref_map);
 +
        /*
         * 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
  
                        strbuf_reset(&note);
                        if (ref) {
 -                              rc |= update_local_ref(ref, what, rm, &note);
 +                              rc |= update_local_ref(ref, what, rm, &note,
 +                                                     summary_width);
                                free(ref);
                        } else
 -                              strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
 -                                          TRANSPORT_SUMMARY_WIDTH,
 -                                          *kind ? kind : "branch",
 -                                          REFCOL_WIDTH,
 -                                          *what ? what : "HEAD");
 +                              format_display(&note, '*',
 +                                             *kind ? kind : "branch", NULL,
 +                                             *what ? what : "HEAD",
 +                                             "FETCH_HEAD", summary_width);
                        if (note.len) {
                                if (verbosity >= 0 && !shown_url) {
                                        fprintf(stderr, _("From %.*s\n"),
  static int quickfetch(struct ref *ref_map)
  {
        struct ref *rm = ref_map;
 +      struct check_connected_options opt = CHECK_CONNECTED_INIT;
  
        /*
         * If we are deepening a shallow clone we already have these
         * really need to perform.  Claiming failure now will ensure
         * we perform the network exchange to deepen our history.
         */
 -      if (depth)
 +      if (deepen)
                return -1;
 -      return check_everything_connected(iterate_ref_map, 1, &rm);
 +      opt.quiet = 1;
 +      return check_connected(iterate_ref_map, &rm, &opt);
  }
  
  static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@@ -917,7 -780,6 +914,7 @@@ static int prune_refs(struct refspec *r
        int url_len, i, result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
        char *url;
 +      int summary_width = transport_summary_width(stale_refs);
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
                : _("   (%s has become dangling)");
                for (ref = stale_refs; ref; ref = ref->next)
                        string_list_append(&refnames, ref->name);
  
 -              result = delete_refs(&refnames);
 +              result = delete_refs(&refnames, 0);
                string_list_clear(&refnames, 0);
        }
  
        if (verbosity >= 0) {
                for (ref = stale_refs; ref; ref = ref->next) {
 +                      struct strbuf sb = STRBUF_INIT;
                        if (!shown_url) {
                                fprintf(stderr, _("From %.*s\n"), url_len, url);
                                shown_url = 1;
                        }
 -                      fprintf(stderr, " x %-*s %-*s -> %s\n",
 -                              TRANSPORT_SUMMARY(_("[deleted]")),
 -                              REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
 +                      format_display(&sb, '-', _("[deleted]"), NULL,
 +                                     _("(none)"), prettify_refname(ref->name),
 +                                     summary_width);
 +                      fprintf(stderr, " %s\n",sb.buf);
 +                      strbuf_release(&sb);
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
@@@ -986,7 -845,7 +983,7 @@@ static int truncate_fetch_head(void
        FILE *fp = fopen_for_writing(filename);
  
        if (!fp)
 -              return error(_("cannot open %s: %s\n"), filename, strerror(errno));
 +              return error_errno(_("cannot open %s"), filename);
        fclose(fp);
        return 0;
  }
@@@ -1002,7 -861,7 +999,7 @@@ static void set_option(struct transpor
                        name, transport->url);
  }
  
 -static struct transport *prepare_transport(struct remote *remote)
 +static struct transport *prepare_transport(struct remote *remote, int deepen)
  {
        struct transport *transport;
        transport = transport_get(remote, NULL);
                set_option(transport, TRANS_OPT_KEEP, "yes");
        if (depth)
                set_option(transport, TRANS_OPT_DEPTH, depth);
 +      if (deepen && deepen_since)
 +              set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
 +      if (deepen && deepen_not.nr)
 +              set_option(transport, TRANS_OPT_DEEPEN_NOT,
 +                         (const char *)&deepen_not);
 +      if (deepen_relative)
 +              set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        return transport;
  
  static void backfill_tags(struct transport *transport, struct ref *ref_map)
  {
 -      if (transport->cannot_reuse) {
 -              gsecondary = prepare_transport(transport->remote);
 +      int cannot_reuse;
 +
 +      /*
 +       * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
 +       * when remote helper is used (setting it to an empty string
 +       * is not unsetting). We could extend the remote helper
 +       * protocol for that, but for now, just force a new connection
 +       * without deepen-since. Similar story for deepen-not.
 +       */
 +      cannot_reuse = transport->cannot_reuse ||
 +              deepen_since || deepen_not.nr;
 +      if (cannot_reuse) {
 +              gsecondary = prepare_transport(transport->remote, 0);
                transport = gsecondary;
        }
  
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
 +      transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
        fetch_refs(transport, ref_map);
  
        if (gsecondary) {
@@@ -1162,7 -1002,7 +1159,7 @@@ static int get_remote_group(const char 
                        size_t wordlen = strcspn(value, " \t\n");
  
                        if (wordlen >= 1)
 -                              string_list_append(g->list,
 +                              string_list_append_nodup(g->list,
                                                   xstrndup(value, wordlen));
                        value += wordlen + (value[wordlen] != '\0');
                }
@@@ -1257,7 -1097,7 +1254,7 @@@ static int fetch_one(struct remote *rem
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
                    "remote name from which new revisions should be fetched."));
  
 -      gtransport = prepare_transport(remote);
 +      gtransport = prepare_transport(remote, 1);
  
        if (prune < 0) {
                /* no command line request */
  int cmd_fetch(int argc, const char **argv, const char *prefix)
  {
        int i;
 -      struct string_list list = STRING_LIST_INIT_NODUP;
 +      struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote;
        int result = 0;
        struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
  
 +      if (deepen_relative) {
 +              if (deepen_relative < 0)
 +                      die(_("Negative depth in --deepen is not supported"));
 +              if (depth)
 +                      die(_("--deepen and --depth are mutually exclusive"));
 +              depth = xstrfmt("%d", deepen_relative);
 +      }
        if (unshallow) {
                if (depth)
                        die(_("--depth and --unshallow cannot be used together"));
        /* no need to be strict, transport_set_option() will validate it again */
        if (depth && atoi(depth) < 1)
                die(_("depth %s is not a positive number"), depth);
 +      if (depth || deepen_since || deepen_not.nr)
 +              deepen = 1;
  
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
                argv_array_clear(&options);
        }
  
 -      /* All names were strdup()ed or strndup()ed */
 -      list.strdup_strings = 1;
        string_list_clear(&list, 0);
  
        close_all_packs();