Merge branch 'jk/fetch-quick-tag-following'
authorJunio C Hamano <gitster@pobox.com>
Wed, 26 Oct 2016 20:14:47 +0000 (13:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Oct 2016 20:14:47 +0000 (13:14 -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 d5329f915e57225e5046f33fae89f6348263f191,3e1a2668c616a2a39a00de318f83dba24ef88ac5..74c0546362d9a6aab185f2a4327cd489a91a3f89
@@@ -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 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,9 -232,10 +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;
  
@@@ -458,132 -452,7 +461,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;
        }
  }
@@@ -737,13 -618,11 +740,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
         * 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)
@@@ -931,21 -809,19 +934,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);
                }
        }
@@@ -991,7 -867,7 +994,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) {
@@@ -1246,7 -1103,7 +1249,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 */
@@@ -1306,13 -1163,6 +1309,13 @@@ int cmd_fetch(int argc, const char **ar
        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) {
diff --combined cache.h
index 5f2f03090fbc343f9e83a1879f20c74804cf6590,68eb8a6415002f90053ee21fe3a9c1bbe279872d..f7ee4145634762a42663df3c69b095d64d919d01
+++ b/cache.h
@@@ -173,7 -173,7 +173,7 @@@ struct cache_entry 
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
 -      unsigned char sha1[20];
 +      struct object_id oid;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -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))
@@@ -409,7 -408,6 +409,7 @@@ static inline enum object_type object_t
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
 +#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
 +#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  
  /*
   * This environment variable is expected to contain a boolean indicating
   */
  extern const char * const local_repo_env[];
  
 +/*
 + * Returns true iff we have a configured git repository (either via
 + * setup_git_directory, or in the environment via $GIT_DIR).
 + */
 +int have_git_dir(void);
 +
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
  extern int is_inside_git_dir(void);
@@@ -477,7 -468,6 +477,7 @@@ extern int get_common_dir_noenv(struct 
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
  extern const char *strip_namespace(const char *namespaced_ref);
 +extern const char *get_super_prefix(void);
  extern const char *get_git_work_tree(void);
  
  /*
@@@ -529,10 -519,9 +529,10 @@@ extern void verify_non_filename(const c
  extern int path_inside_repo(const char *prefix, const char *path);
  
  #define INIT_DB_QUIET 0x0001
 +#define INIT_DB_EXIST_OK 0x0002
  
 -extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 -extern int init_db(const char *template_dir, unsigned int flags);
 +extern int init_db(const char *git_dir, const char *real_git_dir,
 +                 const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
  extern int daemonize(void);
@@@ -592,10 -581,9 +592,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);
@@@ -677,15 -665,8 +677,15 @@@ extern size_t delta_base_cache_limit
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
  
 +/*
 + * Accessors for the core.sharedrepository config which lazy-load the value
 + * from the config (if not already set). The "reset" function can be
 + * used to unset "set" or cached value, meaning that the value will be loaded
 + * fresh from the config file on the next call to get_shared_repository().
 + */
  void set_shared_repository(int value);
  int get_shared_repository(void);
 +void reset_shared_repository(void);
  
  /*
   * Do replace refs need to be checked this run?  This variable is
@@@ -838,8 -819,8 +838,8 @@@ extern void strbuf_git_common_path(stru
        __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 -extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 -                                    const char *fmt, ...)
 +extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 +                                   const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
@@@ -972,39 -953,22 +972,39 @@@ static inline void oidclr(struct object
  #define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 -#define EMPTY_TREE_SHA1_BIN \
 -       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_tree_oid;
 +#define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash)
  
  #define EMPTY_BLOB_SHA1_HEX \
        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
  #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 -#define EMPTY_BLOB_SHA1_BIN \
 -      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_blob_oid;
 +#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
 +
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
  }
  
 +static inline int is_empty_blob_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1040,11 -1004,6 +1040,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,
@@@ -1157,6 -1116,7 +1157,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
@@@ -1175,16 -1135,6 +1176,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
@@@ -1209,11 -1159,6 +1210,11 @@@ struct object_context 
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
  #define GET_SHA1_ONLY_TO_DIE    04000
  
 +#define GET_SHA1_DISAMBIGUATORS \
 +      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 +      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 +      GET_SHA1_BLOB)
 +
  extern int get_sha1(const char *str, unsigned char *sha1);
  extern int get_sha1_commit(const char *str, unsigned char *sha1);
  extern int get_sha1_committish(const char *str, unsigned char *sha1);
@@@ -1228,8 -1173,6 +1229,8 @@@ extern int get_oid(const char *str, str
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
 +extern int set_disambiguate_hint_config(const char *var, const char *value);
 +
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
   * starting at hex.  Write the 20-byte result to sha1 in binary form.
@@@ -1252,12 -1195,11 +1253,12 @@@ 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 */
  
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 -extern int get_sha1_mb(const char *str, unsigned char *sha1);
 +extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
  
@@@ -1367,7 -1309,6 +1368,7 @@@ struct checkout 
                 not_new:1,
                 refresh_cache:1;
  };
 +#define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
@@@ -1393,46 -1334,15 +1394,46 @@@ extern void remove_scheduled_dirs(void)
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
 -      char *name;
 -      char base[FLEX_ARRAY]; /* more */
 +
 +      /* see alt_scratch_buf() */
 +      struct strbuf scratch;
 +      size_t base_len;
 +
 +      char path[FLEX_ARRAY];
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void read_info_alternates(const char * relative_base, int depth);
 -extern void add_to_alternates_file(const char *reference);
 +extern char *compute_alternate_path(const char *path, struct strbuf *err);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern int foreach_alt_odb(alt_odb_fn, void*);
  
 +/*
 + * Allocate a "struct alternate_object_database" but do _not_ actually
 + * add it to the list of alternates.
 + */
 +struct alternate_object_database *alloc_alt_odb(const char *dir);
 +
 +/*
 + * Add the directory to the on-disk alternates file; the new entry will also
 + * take effect in the current process.
 + */
 +extern void add_to_alternates_file(const char *dir);
 +
 +/*
 + * Add the directory to the in-memory list of alternates (along with any
 + * recursive alternates it points to), but do not modify the on-disk alternates
 + * file.
 + */
 +extern void add_to_alternates_memory(const char *dir);
 +
 +/*
 + * Returns a scratch strbuf pre-filled with the alternate object directory,
 + * including a trailing slash, which can be used to access paths in the
 + * alternate. Always use this over direct access to alt->scratch, as it
 + * cleans up any previous use of the scratch buffer.
 + */
 +extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
 +
  struct pack_window {
        struct pack_window *next;
        unsigned char *base;
@@@ -1464,13 -1374,6 +1465,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];
@@@ -1510,6 -1413,7 +1511,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);
  
@@@ -1635,15 -1539,7 +1636,15 @@@ struct object_info 
                } packed;
        } u;
  };
 +
 +/*
 + * Initializer for a "struct object_info" that wants no items. You may
 + * also memset() the memory to all-zeroes.
 + */
 +#define OBJECT_INFO_INIT {NULL}
 +
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1667,18 -1563,10 +1668,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);
@@@ -1720,16 -1608,6 +1721,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);
  
@@@ -1822,8 -1700,6 +1823,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)));
@@@ -1849,6 -1725,7 +1850,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);
@@@ -1860,24 -1737,12 +1861,24 @@@ 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);
 -extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
@@@ -1910,7 -1775,7 +1911,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 266152de36a099bfa75bfaa8429f0234c52a80b1,2fdafa6a996069550b906baa165c59a1db800b33..2eda9291ee307127bdd715f0540994464534429d
@@@ -23,9 -23,6 +23,9 @@@
  #include "bulk-checkin.h"
  #include "streaming.h"
  #include "dir.h"
 +#include "mru.h"
 +#include "list.h"
 +#include "mergesort.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -40,12 -37,6 +40,12 @@@ static inline uintmax_t sz_fmt(size_t s
  
  const unsigned char null_sha1[20];
  const struct object_id null_oid;
 +const struct object_id empty_tree_oid = {
 +      EMPTY_TREE_SHA1_BIN_LITERAL
 +};
 +const struct object_id empty_blob_oid = {
 +      EMPTY_BLOB_SHA1_BIN_LITERAL
 +};
  
  /*
   * This is meant to hold a *small* number of objects that you would
@@@ -68,6 -59,14 +68,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;
@@@ -172,42 -171,36 +172,42 @@@ enum scld_error safe_create_leading_dir
        return result;
  }
  
 -static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
 +static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
  {
        int i;
        for (i = 0; i < 20; i++) {
                static char hex[] = "0123456789abcdef";
                unsigned int val = sha1[i];
 -              char *pos = pathbuf + i*2 + (i > 0);
 -              *pos++ = hex[val >> 4];
 -              *pos = hex[val & 0xf];
 +              strbuf_addch(buf, hex[val >> 4]);
 +              strbuf_addch(buf, hex[val & 0xf]);
 +              if (!i)
 +                      strbuf_addch(buf, '/');
        }
  }
  
  const char *sha1_file_name(const unsigned char *sha1)
  {
 -      static char buf[PATH_MAX];
 -      const char *objdir;
 -      int len;
 +      static struct strbuf buf = STRBUF_INIT;
  
 -      objdir = get_object_directory();
 -      len = strlen(objdir);
 +      strbuf_reset(&buf);
 +      strbuf_addf(&buf, "%s/", get_object_directory());
  
 -      /* '/' + sha1(2) + '/' + sha1(38) + '\0' */
 -      if (len + 43 > PATH_MAX)
 -              die("insanely long object directory %s", objdir);
 -      memcpy(buf, objdir, len);
 -      buf[len] = '/';
 -      buf[len+3] = '/';
 -      buf[len+42] = '\0';
 -      fill_sha1_path(buf + len + 1, sha1);
 -      return buf;
 +      fill_sha1_path(&buf, sha1);
 +      return buf.buf;
 +}
 +
 +struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
 +{
 +      strbuf_setlen(&alt->scratch, alt->base_len);
 +      return &alt->scratch;
 +}
 +
 +static const char *alt_sha1_path(struct alternate_object_database *alt,
 +                               const unsigned char *sha1)
 +{
 +      struct strbuf *buf = alt_scratch_buf(alt);
 +      fill_sha1_path(buf, sha1);
 +      return buf->buf;
  }
  
  /*
@@@ -240,35 -233,6 +240,35 @@@ char *sha1_pack_index_name(const unsign
  struct alternate_object_database *alt_odb_list;
  static struct alternate_object_database **alt_odb_tail;
  
 +/*
 + * Return non-zero iff the path is usable as an alternate object database.
 + */
 +static int alt_odb_usable(struct strbuf *path, const char *normalized_objdir)
 +{
 +      struct alternate_object_database *alt;
 +
 +      /* Detect cases where alternate disappeared */
 +      if (!is_directory(path->buf)) {
 +              error("object directory %s does not exist; "
 +                    "check .git/objects/info/alternates.",
 +                    path->buf);
 +              return 0;
 +      }
 +
 +      /*
 +       * Prevent the common mistake of listing the same
 +       * thing twice, or object directory itself.
 +       */
 +      for (alt = alt_odb_list; alt; alt = alt->next) {
 +              if (!fspathcmp(path->buf, alt->path))
 +                      return 0;
 +      }
 +      if (!fspathcmp(path->buf, normalized_objdir))
 +              return 0;
 +
 +      return 1;
 +}
 +
  /*
   * Prepare alternate object database registry.
   *
@@@ -288,6 -252,8 +288,6 @@@ static int link_alt_odb_entry(const cha
        int depth, const char *normalized_objdir)
  {
        struct alternate_object_database *ent;
 -      struct alternate_object_database *alt;
 -      size_t pfxlen, entlen;
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
        }
        strbuf_addstr(&pathbuf, entry);
  
 -      normalize_path_copy(pathbuf.buf, pathbuf.buf);
 -
 -      pfxlen = strlen(pathbuf.buf);
 +      if (strbuf_normalize_path(&pathbuf) < 0) {
 +              error("unable to normalize alternate object path: %s",
 +                    pathbuf.buf);
 +              strbuf_release(&pathbuf);
 +              return -1;
 +      }
  
        /*
         * The trailing slash after the directory name is given by
         * this function at the end. Remove duplicates.
         */
 -      while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
 -              pfxlen -= 1;
 -
 -      entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */
 -      ent = xmalloc(st_add(sizeof(*ent), entlen));
 -      memcpy(ent->base, pathbuf.buf, pfxlen);
 -      strbuf_release(&pathbuf);
 +      while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
 +              strbuf_setlen(&pathbuf, pathbuf.len - 1);
  
 -      ent->name = ent->base + pfxlen + 1;
 -      ent->base[pfxlen + 3] = '/';
 -      ent->base[pfxlen] = ent->base[entlen-1] = 0;
 -
 -      /* Detect cases where alternate disappeared */
 -      if (!is_directory(ent->base)) {
 -              error("object directory %s does not exist; "
 -                    "check .git/objects/info/alternates.",
 -                    ent->base);
 -              free(ent);
 +      if (!alt_odb_usable(&pathbuf, normalized_objdir)) {
 +              strbuf_release(&pathbuf);
                return -1;
        }
  
 -      /* Prevent the common mistake of listing the same
 -       * thing twice, or object directory itself.
 -       */
 -      for (alt = alt_odb_list; alt; alt = alt->next) {
 -              if (pfxlen == alt->name - alt->base - 1 &&
 -                  !memcmp(ent->base, alt->base, pfxlen)) {
 -                      free(ent);
 -                      return -1;
 -              }
 -      }
 -      if (!fspathcmp(ent->base, normalized_objdir)) {
 -              free(ent);
 -              return -1;
 -      }
 +      ent = alloc_alt_odb(pathbuf.buf);
  
        /* add the alternate entry */
        *alt_odb_tail = ent;
        ent->next = NULL;
  
        /* recursively add alternates */
 -      read_info_alternates(ent->base, depth + 1);
 -
 -      ent->base[pfxlen] = '/';
 +      read_info_alternates(pathbuf.buf, depth + 1);
  
 +      strbuf_release(&pathbuf);
        return 0;
  }
  
@@@ -344,9 -334,7 +344,9 @@@ static void link_alt_odb_entries(const 
        }
  
        strbuf_add_absolute_path(&objdirbuf, get_object_directory());
 -      normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
 +      if (strbuf_normalize_path(&objdirbuf) < 0)
 +              die("unable to normalize object directory: %s",
 +                  objdirbuf.buf);
  
        alt_copy = xmemdupz(alt, len);
        string_list_split_in_place(&entries, alt_copy, sep, -1);
                const char *entry = entries.items[i].string;
                if (entry[0] == '\0' || entry[0] == '#')
                        continue;
 -              if (!is_absolute_path(entry) && depth) {
 -                      error("%s: ignoring relative alternate object store %s",
 -                                      relative_base, entry);
 -              } else {
 -                      link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
 -              }
 +              link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
        }
        string_list_clear(&entries, 0);
        free(alt_copy);
@@@ -387,18 -380,6 +387,18 @@@ void read_info_alternates(const char * 
        munmap(map, mapsz);
  }
  
 +struct alternate_object_database *alloc_alt_odb(const char *dir)
 +{
 +      struct alternate_object_database *ent;
 +
 +      FLEX_ALLOC_STR(ent, path, dir);
 +      strbuf_init(&ent->scratch, 0);
 +      strbuf_addf(&ent->scratch, "%s/", dir);
 +      ent->base_len = ent->scratch.len;
 +
 +      return ent;
 +}
 +
  void add_to_alternates_file(const char *reference)
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        free(alts);
  }
  
 +void add_to_alternates_memory(const char *reference)
 +{
 +      /*
 +       * Make sure alternates are initialized, or else our entry may be
 +       * overwritten when they are.
 +       */
 +      prepare_alt_odb();
 +
 +      link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
 +}
 +
 +/*
 + * Compute the exact path an alternate is at and returns it. In case of
 + * error NULL is returned and the human readable error is added to `err`
 + * `path` may be relative and should point to $GITDIR.
 + * `err` must not be null.
 + */
 +char *compute_alternate_path(const char *path, struct strbuf *err)
 +{
 +      char *ref_git = NULL;
 +      const char *repo, *ref_git_s;
 +      int seen_error = 0;
 +
 +      ref_git_s = real_path_if_valid(path);
 +      if (!ref_git_s) {
 +              seen_error = 1;
 +              strbuf_addf(err, _("path '%s' does not exist"), path);
 +              goto out;
 +      } else
 +              /*
 +               * Beware: read_gitfile(), real_path() and mkpath()
 +               * return static buffer
 +               */
 +              ref_git = xstrdup(ref_git_s);
 +
 +      repo = read_gitfile(ref_git);
 +      if (!repo)
 +              repo = read_gitfile(mkpath("%s/.git", ref_git));
 +      if (repo) {
 +              free(ref_git);
 +              ref_git = xstrdup(repo);
 +      }
 +
 +      if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
 +              char *ref_git_git = mkpathdup("%s/.git", ref_git);
 +              free(ref_git);
 +              ref_git = ref_git_git;
 +      } else if (!is_directory(mkpath("%s/objects", ref_git))) {
 +              struct strbuf sb = STRBUF_INIT;
 +              seen_error = 1;
 +              if (get_common_dir(&sb, ref_git)) {
 +                      strbuf_addf(err,
 +                                  _("reference repository '%s' as a linked "
 +                                    "checkout is not supported yet."),
 +                                  path);
 +                      goto out;
 +              }
 +
 +              strbuf_addf(err, _("reference repository '%s' is not a "
 +                                      "local repository."), path);
 +              goto out;
 +      }
 +
 +      if (!access(mkpath("%s/shallow", ref_git), F_OK)) {
 +              strbuf_addf(err, _("reference repository '%s' is shallow"),
 +                          path);
 +              seen_error = 1;
 +              goto out;
 +      }
 +
 +      if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) {
 +              strbuf_addf(err,
 +                          _("reference repository '%s' is grafted"),
 +                          path);
 +              seen_error = 1;
 +              goto out;
 +      }
 +
 +out:
 +      if (seen_error) {
 +              free(ref_git);
 +              ref_git = NULL;
 +      }
 +
 +      return ref_git;
 +}
 +
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
  {
        struct alternate_object_database *ent;
@@@ -595,8 -489,8 +595,8 @@@ static int check_and_freshen_nonlocal(c
        struct alternate_object_database *alt;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              fill_sha1_path(alt->name, sha1);
 -              if (check_and_freshen_file(alt->base, freshen))
 +              const char *path = alt_sha1_path(alt, sha1);
 +              if (check_and_freshen_file(path, freshen))
                        return 1;
        }
        return 0;
@@@ -628,9 -522,6 +628,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,
@@@ -904,7 -795,7 +904,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);
  }
@@@ -1000,6 -891,36 +1000,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
@@@ -1410,20 -1331,10 +1410,20 @@@ static void prepare_packed_git_one(cha
        strbuf_release(&path);
  }
  
 +static void *get_next_packed_git(const void *p)
 +{
 +      return ((const struct packed_git *)p)->next;
 +}
 +
 +static void set_next_packed_git(void *p, void *next)
 +{
 +      ((struct packed_git *)p)->next = next;
 +}
 +
  static int sort_pack(const void *a_, const void *b_)
  {
 -      struct packed_git *a = *((struct packed_git **)a_);
 -      struct packed_git *b = *((struct packed_git **)b_);
 +      const struct packed_git *a = a_;
 +      const struct packed_git *b = b_;
        int st;
  
        /*
  
  static void rearrange_packed_git(void)
  {
 -      struct packed_git **ary, *p;
 -      int i, n;
 -
 -      for (n = 0, p = packed_git; p; p = p->next)
 -              n++;
 -      if (n < 2)
 -              return;
 -
 -      /* prepare an array of packed_git for easier sorting */
 -      ary = xcalloc(n, sizeof(struct packed_git *));
 -      for (n = 0, p = packed_git; p; p = p->next)
 -              ary[n++] = p;
 -
 -      qsort(ary, n, sizeof(struct packed_git *), sort_pack);
 +      packed_git = llist_mergesort(packed_git, get_next_packed_git,
 +                                   set_next_packed_git, sort_pack);
 +}
  
 -      /* link them back again */
 -      for (i = 0; i < n - 1; i++)
 -              ary[i]->next = ary[i + 1];
 -      ary[n - 1]->next = NULL;
 -      packed_git = ary[0];
 +static void prepare_packed_git_mru(void)
 +{
 +      struct packed_git *p;
  
 -      free(ary);
 +      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;
@@@ -1472,10 -1394,12 +1472,10 @@@ void prepare_packed_git(void
                return;
        prepare_packed_git_one(get_object_directory(), 1);
        prepare_alt_odb();
 -      for (alt = alt_odb_list; alt; alt = alt->next) {
 -              alt->name[-1] = 0;
 -              prepare_packed_git_one(alt->base, 0);
 -              alt->name[-1] = '/';
 -      }
 +      for (alt = alt_odb_list; alt; alt = alt->next)
 +              prepare_packed_git_one(alt->path, 0);
        rearrange_packed_git();
 +      prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
  }
  
@@@ -1591,8 -1515,8 +1591,8 @@@ static int stat_sha1_file(const unsigne
        prepare_alt_odb();
        errno = ENOENT;
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              fill_sha1_path(alt->name, sha1);
 -              if (!lstat(alt->base, st))
 +              const char *path = alt_sha1_path(alt, sha1);
 +              if (!lstat(path, st))
                        return 0;
        }
  
@@@ -1612,8 -1536,8 +1612,8 @@@ static int open_sha1_file(const unsigne
  
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              fill_sha1_path(alt->name, sha1);
 -              fd = git_open_noatime(alt->base);
 +              const char *path = alt_sha1_path(alt, sha1);
 +              fd = git_open_noatime(path);
                if (fd >= 0)
                        return fd;
                if (most_interesting_errno == ENOENT)
@@@ -1672,9 -1596,7 +1672,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.
@@@ -1802,8 -1706,6 +1802,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))
  
  int parse_sha1_header(const char *hdr, unsigned long *sizep)
  {
 -      struct object_info oi;
 +      struct object_info oi = OBJECT_INFO_INIT;
  
        oi.sizep = sizep;
 -      oi.typename = NULL;
 -      oi.typep = NULL;
        return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
  }
  
@@@ -2092,8 -1996,8 +2092,8 @@@ unwind
        goto out;
  }
  
 -static int packed_object_info(struct packed_git *p, off_t obj_offset,
 -                            struct object_info *oi)
 +int packed_object_info(struct packed_git *p, off_t obj_offset,
 +                     struct object_info *oi)
  {
        struct pack_window *w_curs = NULL;
        unsigned long size;
@@@ -2193,142 -2097,136 +2193,142 @@@ static void *unpack_compressed_entry(st
        return buffer;
  }
  
 -#define MAX_DELTA_CACHE (256)
 -
 +static struct hashmap delta_base_cache;
  static size_t delta_base_cached;
  
 -static struct delta_base_cache_lru_list {
 -      struct delta_base_cache_lru_list *prev;
 -      struct delta_base_cache_lru_list *next;
 -} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
 +static LIST_HEAD(delta_base_cache_lru);
  
 -static struct delta_base_cache_entry {
 -      struct delta_base_cache_lru_list lru;
 -      void *data;
 +struct delta_base_cache_key {
        struct packed_git *p;
        off_t base_offset;
 +};
 +
 +struct delta_base_cache_entry {
 +      struct hashmap hash;
 +      struct delta_base_cache_key key;
 +      struct list_head lru;
 +      void *data;
        unsigned long size;
        enum object_type type;
 -} delta_base_cache[MAX_DELTA_CACHE];
 +};
  
 -static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
 +static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)
  {
 -      unsigned long hash;
 +      unsigned int hash;
  
 -      hash = (unsigned long)(intptr_t)p + (unsigned long)base_offset;
 +      hash = (unsigned int)(intptr_t)p + (unsigned int)base_offset;
        hash += (hash >> 8) + (hash >> 16);
 -      return hash % MAX_DELTA_CACHE;
 +      return hash;
  }
  
  static struct delta_base_cache_entry *
  get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
  {
 -      unsigned long hash = pack_entry_hash(p, base_offset);
 -      return delta_base_cache + hash;
 +      struct hashmap_entry entry;
 +      struct delta_base_cache_key key;
 +
 +      if (!delta_base_cache.cmpfn)
 +              return NULL;
 +
 +      hashmap_entry_init(&entry, pack_entry_hash(p, base_offset));
 +      key.p = p;
 +      key.base_offset = base_offset;
 +      return hashmap_get(&delta_base_cache, &entry, &key);
 +}
 +
 +static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
 +                                 const struct delta_base_cache_key *b)
 +{
 +      return a->p == b->p && a->base_offset == b->base_offset;
  }
  
 -static int eq_delta_base_cache_entry(struct delta_base_cache_entry *ent,
 -                                   struct packed_git *p, off_t base_offset)
 +static int delta_base_cache_hash_cmp(const void *va, const void *vb,
 +                                   const void *vkey)
  {
 -      return (ent->data && ent->p == p && ent->base_offset == base_offset);
 +      const struct delta_base_cache_entry *a = va, *b = vb;
 +      const struct delta_base_cache_key *key = vkey;
 +      if (key)
 +              return !delta_base_cache_key_eq(&a->key, key);
 +      else
 +              return !delta_base_cache_key_eq(&a->key, &b->key);
  }
  
  static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
  {
 -      struct delta_base_cache_entry *ent;
 -      ent = get_delta_base_cache_entry(p, base_offset);
 -      return eq_delta_base_cache_entry(ent, p, base_offset);
 +      return !!get_delta_base_cache_entry(p, base_offset);
  }
  
 -static void clear_delta_base_cache_entry(struct delta_base_cache_entry *ent)
 +/*
 + * Remove the entry from the cache, but do _not_ free the associated
 + * entry data. The caller takes ownership of the "data" buffer, and
 + * should copy out any fields it wants before detaching.
 + */
 +static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)
  {
 -      ent->data = NULL;
 -      ent->lru.next->prev = ent->lru.prev;
 -      ent->lru.prev->next = ent->lru.next;
 +      hashmap_remove(&delta_base_cache, ent, &ent->key);
 +      list_del(&ent->lru);
        delta_base_cached -= ent->size;
 +      free(ent);
  }
  
  static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 -      unsigned long *base_size, enum object_type *type, int keep_cache)
 +      unsigned long *base_size, enum object_type *type)
  {
        struct delta_base_cache_entry *ent;
 -      void *ret;
  
        ent = get_delta_base_cache_entry(p, base_offset);
 -
 -      if (!eq_delta_base_cache_entry(ent, p, base_offset))
 +      if (!ent)
                return unpack_entry(p, base_offset, type, base_size);
  
 -      ret = ent->data;
 -
 -      if (!keep_cache)
 -              clear_delta_base_cache_entry(ent);
 -      else
 -              ret = xmemdupz(ent->data, ent->size);
        *type = ent->type;
        *base_size = ent->size;
 -      return ret;
 +      return xmemdupz(ent->data, ent->size);
  }
  
  static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
  {
 -      if (ent->data) {
 -              free(ent->data);
 -              ent->data = NULL;
 -              ent->lru.next->prev = ent->lru.prev;
 -              ent->lru.prev->next = ent->lru.next;
 -              delta_base_cached -= ent->size;
 -      }
 +      free(ent->data);
 +      detach_delta_base_cache_entry(ent);
  }
  
  void clear_delta_base_cache(void)
  {
 -      unsigned long p;
 -      for (p = 0; p < MAX_DELTA_CACHE; p++)
 -              release_delta_base_cache(&delta_base_cache[p]);
 +      struct hashmap_iter iter;
 +      struct delta_base_cache_entry *entry;
 +      for (entry = hashmap_iter_first(&delta_base_cache, &iter);
 +           entry;
 +           entry = hashmap_iter_next(&iter)) {
 +              release_delta_base_cache(entry);
 +      }
  }
  
  static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        void *base, unsigned long base_size, enum object_type type)
  {
 -      unsigned long hash = pack_entry_hash(p, base_offset);
 -      struct delta_base_cache_entry *ent = delta_base_cache + hash;
 -      struct delta_base_cache_lru_list *lru;
 +      struct delta_base_cache_entry *ent = xmalloc(sizeof(*ent));
 +      struct list_head *lru, *tmp;
  
 -      release_delta_base_cache(ent);
        delta_base_cached += base_size;
  
 -      for (lru = delta_base_cache_lru.next;
 -           delta_base_cached > delta_base_cache_limit
 -           && lru != &delta_base_cache_lru;
 -           lru = lru->next) {
 -              struct delta_base_cache_entry *f = (void *)lru;
 -              if (f->type == OBJ_BLOB)
 -                      release_delta_base_cache(f);
 -      }
 -      for (lru = delta_base_cache_lru.next;
 -           delta_base_cached > delta_base_cache_limit
 -           && lru != &delta_base_cache_lru;
 -           lru = lru->next) {
 -              struct delta_base_cache_entry *f = (void *)lru;
 +      list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
 +              struct delta_base_cache_entry *f =
 +                      list_entry(lru, struct delta_base_cache_entry, lru);
 +              if (delta_base_cached <= delta_base_cache_limit)
 +                      break;
                release_delta_base_cache(f);
        }
  
 -      ent->p = p;
 -      ent->base_offset = base_offset;
 +      ent->key.p = p;
 +      ent->key.base_offset = base_offset;
        ent->type = type;
        ent->data = base;
        ent->size = base_size;
 -      ent->lru.next = &delta_base_cache_lru;
 -      ent->lru.prev = delta_base_cache_lru.prev;
 -      delta_base_cache_lru.prev->next = &ent->lru;
 -      delta_base_cache_lru.prev = &ent->lru;
 +      list_add_tail(&ent->lru, &delta_base_cache_lru);
 +
 +      if (!delta_base_cache.cmpfn)
 +              hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, 0);
 +      hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
 +      hashmap_add(&delta_base_cache, ent);
  }
  
  static void *read_object(const unsigned char *sha1, enum object_type *type,
@@@ -2372,11 -2270,11 +2372,11 @@@ void *unpack_entry(struct packed_git *p
                struct delta_base_cache_entry *ent;
  
                ent = get_delta_base_cache_entry(p, curpos);
 -              if (eq_delta_base_cache_entry(ent, p, curpos)) {
 +              if (ent) {
                        type = ent->type;
                        data = ent->data;
                        size = ent->size;
 -                      clear_delta_base_cache_entry(ent);
 +                      detach_delta_base_cache_entry(ent);
                        base_from_cache = 1;
                        break;
                }
        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:
@@@ -2706,15 -2604,21 +2706,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;
                }
        }
@@@ -2864,7 -2768,7 +2864,7 @@@ int sha1_object_info_extended(const uns
  int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
  {
        enum object_type type;
 -      struct object_info oi = {NULL};
 +      struct object_info oi = OBJECT_INFO_INIT;
  
        oi.typep = &type;
        oi.sizep = sizep;
@@@ -2881,7 -2785,7 +2881,7 @@@ static void *read_packed_sha1(const uns
  
        if (!find_pack_entry(sha1, &e))
                return NULL;
 -      data = cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 +      data = cache_or_unpack_entry(e.p, e.offset, size, type);
        if (!data) {
                /*
                 * We're probably in deep shit, but let's try to fetch
@@@ -3335,6 -3239,11 +3335,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;
@@@ -3674,7 -3583,8 +3679,7 @@@ static int loose_from_alt_odb(struct al
        struct strbuf buf = STRBUF_INIT;
        int r;
  
 -      /* copy base not including trailing '/' */
 -      strbuf_add(&buf, alt->base, alt->name - alt->base - 1);
 +      strbuf_addstr(&buf, alt->path);
        r = for_each_loose_file_in_objdir_buf(&buf,
                                              data->cb, NULL, NULL,
                                              data->data);