Merge branch 'jk/fetch-quick-tag-following' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 28 Oct 2016 16:01:17 +0000 (09:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 28 Oct 2016 16:01:17 +0000 (09:01 -0700)
When fetching from a remote that has many tags that are irrelevant
to branches we are following, we used to waste way too many cycles
when checking if the object pointed at by a tag (that we are not
going to fetch!) exists in our repository too carefully.

* jk/fetch-quick-tag-following:
fetch: use "quick" has_sha1_file for tag following

1  2 
builtin/fetch.c
cache.h
sha1_file.c
diff --combined builtin/fetch.c
index 164623bb6f2eb3ffad3eac59b8ec440b9cf60d9c,3e1a2668c616a2a39a00de318f83dba24ef88ac5..cd7e3cefe62b98786097de9a6a456589060c7ebd
@@@ -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>...]]"),
@@@ -233,9 -232,10 +233,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;
  
@@@ -450,132 -452,7 +453,132 @@@ 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)
 +{
 +      strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(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,
  
        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);
                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);
                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);
                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);
                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);
                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);
                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);
                return 1;
        }
  }
@@@ -729,13 -618,11 +732,13 @@@ static int store_updated_refs(const cha
                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
                                rc |= update_local_ref(ref, what, rm, &note);
                                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");
                        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
         */
        if (depth)
                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)
@@@ -923,21 -809,19 +926,21 @@@ static int prune_refs(struct refspec *r
                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));
 +                      fprintf(stderr, " %s\n",sb.buf);
 +                      strbuf_release(&sb);
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
diff --combined cache.h
index 4cba08ecb1096dfdbcaae0eb82adad4a7825d35d,68eb8a6415002f90053ee21fe3a9c1bbe279872d..1ec9021a70083ea40ee21add8ba766ea9b9c5e25
+++ b/cache.h
@@@ -367,9 -367,8 +367,9 @@@ extern void free_name_hash(struct index
  #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 -#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
 -#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
 +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
 +#define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@@ -582,10 -581,9 +582,10 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 -extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
 -extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 +extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
 +extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
  extern int index_name_is_other(const struct index_state *, const char *, int);
@@@ -1006,11 -1004,6 +1006,11 @@@ int adjust_shared_perm(const char *path
   * directory while we were working.  To be robust against this kind of
   * race, callers might want to try invoking the function again when it
   * returns SCLD_VANISHED.
 + *
 + * safe_create_leading_directories() temporarily changes path while it
 + * is working but restores it before returning.
 + * safe_create_leading_directories_const() doesn't modify path, even
 + * temporarily.
   */
  enum scld_error {
        SCLD_OK = 0,
@@@ -1123,6 -1116,7 +1123,7 @@@ static inline int has_sha1_file(const u
  
  /* Same as the above, except for struct object_id. */
  extern int has_object_file(const struct object_id *oid);
+ extern int has_object_file_with_flags(const struct object_id *oid, int flags);
  
  /*
   * Return true iff an alternate object database has a loose object
@@@ -1141,16 -1135,6 +1142,16 @@@ static inline unsigned int hexval(unsig
        return hexval_table[c];
  }
  
 +/*
 + * Convert two consecutive hexadecimal digits into a char.  Return a
 + * negative value on error.  Don't run over the end of short strings.
 + */
 +static inline int hex2chr(const char *s)
 +{
 +      int val = hexval(s[0]);
 +      return (val < 0) ? val : (val << 4) | hexval(s[1]);
 +}
 +
  /* Convert to/from hex/sha1 representation */
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
@@@ -1211,7 -1195,6 +1212,7 @@@ extern int get_oid_hex(const char *hex
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
  extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +extern char *oid_to_hex_r(char *out, const struct object_id *oid);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
@@@ -1391,13 -1374,6 +1392,13 @@@ extern struct packed_git 
        char pack_name[FLEX_ARRAY]; /* more */
  } *packed_git;
  
 +/*
 + * A most-recently-used ordered version of the packed_git list, which can
 + * be iterated instead of packed_git (and marked via mru_mark).
 + */
 +struct mru;
 +extern struct mru *packed_git_mru;
 +
  struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
@@@ -1437,6 -1413,7 +1438,6 @@@ extern unsigned char *use_pack(struct p
  extern void close_pack_windows(struct packed_git *);
  extern void close_all_packs(void);
  extern void unuse_pack(struct pack_window **);
 -extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  
@@@ -1586,18 -1563,10 +1587,18 @@@ struct git_config_source 
        const char *blob;
  };
  
 +enum config_origin_type {
 +      CONFIG_ORIGIN_BLOB,
 +      CONFIG_ORIGIN_FILE,
 +      CONFIG_ORIGIN_STDIN,
 +      CONFIG_ORIGIN_SUBMODULE_BLOB,
 +      CONFIG_ORIGIN_CMDLINE
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 -extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
@@@ -1639,16 -1608,6 +1640,16 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
 +enum config_scope {
 +      CONFIG_SCOPE_UNKNOWN = 0,
 +      CONFIG_SCOPE_SYSTEM,
 +      CONFIG_SCOPE_GLOBAL,
 +      CONFIG_SCOPE_REPO,
 +      CONFIG_SCOPE_CMDLINE,
 +};
 +
 +extern enum config_scope current_config_scope(void);
  extern const char *current_config_origin_type(void);
  extern const char *current_config_name(void);
  
@@@ -1741,8 -1700,6 +1742,8 @@@ extern int ignore_untracked_cache_confi
  struct key_value_info {
        const char *filename;
        int linenr;
 +      enum config_origin_type origin_type;
 +      enum config_scope scope;
  };
  
  extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@@ -1768,6 -1725,7 +1769,6 @@@ extern int copy_file(const char *dst, c
  extern int copy_file_with_time(const char *dst, const char *src, int mode);
  
  extern void write_or_die(int fd, const void *buf, size_t count);
 -extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
  extern void fsync_or_die(int fd, const char *);
  
  extern ssize_t read_in_full(int fd, void *buf, size_t count);
@@@ -1779,21 -1737,8 +1780,21 @@@ static inline ssize_t write_str_in_full
        return write_in_full(fd, str, strlen(str));
  }
  
 -extern int write_file(const char *path, const char *fmt, ...);
 -extern int write_file_gently(const char *path, const char *fmt, ...);
 +/**
 + * Open (and truncate) the file at path, write the contents of buf to it,
 + * and close it. Dies if any errors are encountered.
 + */
 +extern void write_file_buf(const char *path, const char *buf, size_t len);
 +
 +/**
 + * Like write_file_buf(), but format the contents into a buffer first.
 + * Additionally, write_file() will append a newline if one is not already
 + * present, making it convenient to write text files:
 + *
 + *   write_file(path, "counter: %d", ctr);
 + */
 +__attribute__((format (printf, 2, 3)))
 +extern void write_file(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
@@@ -1830,7 -1775,7 +1831,7 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
 -int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
diff --combined sha1_file.c
index 7a4f8f8661a94ed9381e1297118c0de9e90c8c97,2fdafa6a996069550b906baa165c59a1db800b33..727a9769fb7549e365e66f4ecb65f8e665898a96
@@@ -23,7 -23,6 +23,7 @@@
  #include "bulk-checkin.h"
  #include "streaming.h"
  #include "dir.h"
 +#include "mru.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -60,6 -59,14 +60,6 @@@ static struct cached_object empty_tree 
        0
  };
  
 -/*
 - * A pointer to the last packed_git in which an object was found.
 - * When an object is sought, we look in this packfile first, because
 - * objects that are looked up at similar times are often in the same
 - * packfile as one another.
 - */
 -static struct packed_git *last_found_pack;
 -
  static struct cached_object *find_cached_object(const unsigned char *sha1)
  {
        int i;
@@@ -515,9 -522,6 +515,9 @@@ static size_t peak_pack_mapped
  static size_t pack_mapped;
  struct packed_git *packed_git;
  
 +static struct mru packed_git_mru_storage;
 +struct mru *packed_git_mru = &packed_git_mru_storage;
 +
  void pack_report(void)
  {
        fprintf(stderr,
@@@ -791,7 -795,7 +791,7 @@@ void close_all_packs(void
  
        for (p = packed_git; p; p = p->next)
                if (p->do_not_close)
 -                      die("BUG! Want to close pack marked 'do-not-close'");
 +                      die("BUG: want to close pack marked 'do-not-close'");
                else
                        close_pack(p);
  }
@@@ -887,6 -891,36 +887,6 @@@ void close_pack_index(struct packed_gi
        }
  }
  
 -/*
 - * This is used by git-repack in case a newly created pack happens to
 - * contain the same set of objects as an existing one.  In that case
 - * the resulting file might be different even if its name would be the
 - * same.  It is best to close any reference to the old pack before it is
 - * replaced on disk.  Of course no index pointers or windows for given pack
 - * must subsist at this point.  If ever objects from this pack are requested
 - * again, the new version of the pack will be reinitialized through
 - * reprepare_packed_git().
 - */
 -void free_pack_by_name(const char *pack_name)
 -{
 -      struct packed_git *p, **pp = &packed_git;
 -
 -      while (*pp) {
 -              p = *pp;
 -              if (strcmp(pack_name, p->pack_name) == 0) {
 -                      clear_delta_base_cache();
 -                      close_pack(p);
 -                      free(p->bad_object_sha1);
 -                      *pp = p->next;
 -                      if (last_found_pack == p)
 -                              last_found_pack = NULL;
 -                      free(p);
 -                      return;
 -              }
 -              pp = &p->next;
 -      }
 -}
 -
  static unsigned int get_max_fd_limit(void)
  {
  #ifdef RLIMIT_NOFILE
@@@ -1351,15 -1385,6 +1351,15 @@@ static void rearrange_packed_git(void
        free(ary);
  }
  
 +static void prepare_packed_git_mru(void)
 +{
 +      struct packed_git *p;
 +
 +      mru_clear(packed_git_mru);
 +      for (p = packed_git; p; p = p->next)
 +              mru_append(packed_git_mru, p);
 +}
 +
  static int prepare_packed_git_run_once = 0;
  void prepare_packed_git(void)
  {
                alt->name[-1] = '/';
        }
        rearrange_packed_git();
 +      prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
  }
  
@@@ -1572,9 -1596,7 +1572,9 @@@ unsigned long unpack_object_header_buff
        return used;
  }
  
 -int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 +static int unpack_sha1_short_header(git_zstream *stream,
 +                                  unsigned char *map, unsigned long mapsize,
 +                                  void *buffer, unsigned long bufsiz)
  {
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
        return git_inflate(stream, 0);
  }
  
 +int unpack_sha1_header(git_zstream *stream,
 +                     unsigned char *map, unsigned long mapsize,
 +                     void *buffer, unsigned long bufsiz)
 +{
 +      int status = unpack_sha1_short_header(stream, map, mapsize,
 +                                            buffer, bufsiz);
 +
 +      if (status < Z_OK)
 +              return status;
 +
 +      /* Make sure we have the terminating NUL */
 +      if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +              return -1;
 +      return 0;
 +}
 +
  static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
                                        unsigned long mapsize, void *buffer,
                                        unsigned long bufsiz, struct strbuf *header)
  {
        int status;
  
 -      status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
 +      status = unpack_sha1_short_header(stream, map, mapsize, buffer, bufsiz);
 +      if (status < Z_OK)
 +              return -1;
  
        /*
         * Check if entire header is unpacked in the first iteration.
@@@ -1702,8 -1706,6 +1702,8 @@@ static int parse_sha1_header_extended(c
         */
        for (;;) {
                char c = *hdr++;
 +              if (!c)
 +                      return -1;
                if (c == ' ')
                        break;
                type_len++;
                strbuf_add(oi->typename, type_buf, type_len);
        /*
         * Set type to 0 if its an unknown object and
 -       * we're obtaining the type using '--allow-unkown-type'
 +       * we're obtaining the type using '--allow-unknown-type'
         * option.
         */
        if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
@@@ -2328,7 -2330,7 +2328,7 @@@ void *unpack_entry(struct packed_git *p
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
                if (data)
 -                      die("BUG in unpack_entry: left loop at a valid delta");
 +                      die("BUG: unpack_entry: left loop at a valid delta");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@@ -2602,15 -2604,21 +2602,15 @@@ static int fill_pack_entry(const unsign
   */
  static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
  {
 -      struct packed_git *p;
 +      struct mru_entry *p;
  
        prepare_packed_git();
        if (!packed_git)
                return 0;
  
 -      if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
 -              return 1;
 -
 -      for (p = packed_git; p; p = p->next) {
 -              if (p == last_found_pack)
 -                      continue; /* we already checked this one */
 -
 -              if (fill_pack_entry(sha1, e, p)) {
 -                      last_found_pack = p;
 +      for (p = packed_git_mru->head; p; p = p->next) {
 +              if (fill_pack_entry(sha1, e, p->item)) {
 +                      mru_mark(packed_git_mru, p);
                        return 1;
                }
        }
@@@ -3231,6 -3239,11 +3231,11 @@@ int has_object_file(const struct object
        return has_sha1_file(oid->hash);
  }
  
+ int has_object_file_with_flags(const struct object_id *oid, int flags)
+ {
+       return has_sha1_file_with_flags(oid->hash, flags);
+ }
  static void check_tree(const void *buf, size_t size)
  {
        struct tree_desc desc;