Merge branch 'sb/sha1-file-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Wed, 23 Aug 2017 21:13:10 +0000 (14:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 23 Aug 2017 21:13:10 +0000 (14:13 -0700)
Code clean-up.

* sb/sha1-file-cleanup:
sha1_file: make read_info_alternates static

1  2 
cache.h
sha1_file.c
diff --combined cache.h
index f99404f89ad1fa158b67cc671abad5968fdc1e98,11a2bcd38b3e33faed9ea7c655c878997423c89a..ab3b52ff926083cad9edab1c4cd90cbfdcaeae9c
+++ b/cache.h
  #include "trace.h"
  #include "string-list.h"
  #include "pack-revindex.h"
 +#include "hash.h"
 +#include "path.h"
 +#include "sha1-array.h"
  
 -#include SHA1_HEADER
  #ifndef platform_SHA_CTX
  /*
   * platform's underlying implementation of SHA-1; could be OpenSSL,
@@@ -68,12 -66,8 +68,12 @@@ unsigned long git_deflate_bound(git_zst
  #define GIT_SHA1_RAWSZ 20
  #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
  
 +/* The length in byte and in hex digits of the largest possible hash value. */
 +#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
 +#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
 +
  struct object_id {
 -      unsigned char hash[GIT_SHA1_RAWSZ];
 +      unsigned char hash[GIT_MAX_RAWSZ];
  };
  
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
@@@ -349,7 -343,6 +349,7 @@@ struct index_state 
  extern struct index_state the_index;
  
  /* Name hashing */
 +extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void free_name_hash(struct index_state *istate);
@@@ -416,7 -409,6 +416,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 GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 +#define GITMODULES_FILE ".gitmodules"
  #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
  #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #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[];
  
 +extern void setup_git_env(void);
 +
  /*
   * Returns true iff we have a configured git repository (either via
   * setup_git_directory, or in the environment via $GIT_DIR).
@@@ -487,7 -475,6 +487,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);
  
  /*
@@@ -517,44 -504,20 +517,44 @@@ extern int is_nonbare_repository_dir(st
  #define READ_GITFILE_ERR_NO_PATH 6
  #define READ_GITFILE_ERR_NOT_A_REPO 7
  #define READ_GITFILE_ERR_TOO_LARGE 8
 +extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
  extern const char *read_gitfile_gently(const char *path, int *return_error_code);
  #define read_gitfile(path) read_gitfile_gently((path), NULL)
 -extern const char *resolve_gitdir(const char *suspect);
 +extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 +#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 +
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
 -extern const char **get_pathspec(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
 +/*
 + * Find the commondir and gitdir of the repository that contains the current
 + * working directory, without changing the working directory or other global
 + * state. The result is appended to commondir and gitdir.  If the discovered
 + * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
 + * both have the same result appended to the buffer.  The return value is
 + * either 0 upon success and non-zero if no repository was found.
 + */
 +extern int discover_git_directory(struct strbuf *commondir,
 +                                struct strbuf *gitdir);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern char *prefix_path(const char *prefix, int len, const char *path);
  extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 -extern const char *prefix_filename(const char *prefix, int len, const char *path);
 +
 +/*
 + * Concatenate "prefix" (if len is non-zero) and "path", with no
 + * connecting characters (so "prefix" should end with a "/").
 + * Unlike prefix_path, this should be used if the named file does
 + * not have to interact with index entry; i.e. name of a random file
 + * on the filesystem.
 + *
 + * The return value is always a newly allocated string (even if the
 + * prefix was empty).
 + */
 +extern char *prefix_filename(const char *prefix, const char *path);
 +
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix,
                            const char *name,
@@@ -604,33 -567,12 +604,33 @@@ extern int read_index_unmerged(struct i
  #define CLOSE_LOCK            (1 << 1)
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  extern int discard_index(struct index_state *);
 +extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 +extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern void adjust_dirname_case(struct index_state *istate, char *name);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 +
 +/*
 + * Searches for an entry defined by name and namelen in the given index.
 + * If the return value is positive (including 0) it is the position of an
 + * exact match. If the return value is negative, the negated value minus 1
 + * is the position where the entry would be inserted.
 + * Example: The current index consists of these files and its stages:
 + *
 + *   b#0, d#0, f#1, f#3
 + *
 + * index_name_pos(&index, "a", 1) -> -1
 + * index_name_pos(&index, "b", 1) ->  0
 + * index_name_pos(&index, "c", 1) -> -2
 + * index_name_pos(&index, "d", 1) ->  1
 + * index_name_pos(&index, "e", 1) -> -3
 + * index_name_pos(&index, "f", 1) -> -3
 + * index_name_pos(&index, "g", 1) -> -5
 + */
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 +
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 +
 +/* Remove entry, return true if there are more entries to go. */
  extern int remove_index_entry_at(struct index_state *, int pos);
 +
  extern void remove_marked_cache_entries(struct index_state *istate);
  extern int remove_file_from_index(struct index_state *, const char *path);
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 +/*
 + * These two are used to add the contents of the file at path
 + * to the index, marking the working tree up-to-date by storing
 + * the cached stat info in the resulting cache entry.  A caller
 + * that has already run lstat(2) on the path can call
 + * add_to_index(), and all others can call add_file_to_index();
 + * the latter will do necessary lstat(2) internally before
 + * calling the former.
 + */
  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);
 -extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 +extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -719,8 -648,6 +719,8 @@@ extern void update_index_if_able(struc
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
 +extern int verify_index_checksum;
 +
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
  extern int trust_ctime;
@@@ -731,6 -658,7 +731,6 @@@ extern int minimum_abbrev, default_abbr
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
 -extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
  extern int warn_on_object_refname_ambiguity;
  extern const char *apply_default_whitespace;
@@@ -739,7 -667,7 +739,7 @@@ extern const char *git_attributes_file
  extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
 -extern int core_compression_seen;
 +extern int pack_compression_level;
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
@@@ -773,6 -701,7 +773,6 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 -extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
  
  /*
   * Include broken refs in all ref iterations, which will
@@@ -797,14 -726,6 +797,14 @@@ enum hide_dotfiles_type 
  };
  extern enum hide_dotfiles_type hide_dotfiles;
  
 +enum log_refs_config {
 +      LOG_REFS_UNSET = -1,
 +      LOG_REFS_NONE = 0,
 +      LOG_REFS_NORMAL,
 +      LOG_REFS_ALWAYS
 +};
 +extern enum log_refs_config log_all_ref_updates;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -894,6 -815,64 +894,6 @@@ extern void check_repository_format(voi
  #define DATA_CHANGED    0x0020
  #define TYPE_CHANGED    0x0040
  
 -/*
 - * Return a statically allocated filename, either generically (mkpath), in
 - * the repository directory (git_path), or in a submodule's repository
 - * directory (git_path_submodule). In all cases, note that the result
 - * may be overwritten by another call to _any_ of the functions. Consider
 - * using the safer "dup" or "strbuf" formats below (in some cases, the
 - * unsafe versions have already been removed).
 - */
 -extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 -
 -extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 -      __attribute__((format (printf, 3, 4)));
 -extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 -      __attribute__((format (printf, 2, 3)));
 -extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
 -      __attribute__((format (printf, 2, 3)));
 -extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 -      __attribute__((format (printf, 2, 3)));
 -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)));
 -extern char *mkpathdup(const char *fmt, ...)
 -      __attribute__((format (printf, 1, 2)));
 -extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 -      __attribute__((format (printf, 2, 3)));
 -
 -extern void report_linked_checkout_garbage(void);
 -
 -/*
 - * You can define a static memoized git path like:
 - *
 - *    static GIT_PATH_FUNC(git_path_foo, "FOO");
 - *
 - * or use one of the global ones below.
 - */
 -#define GIT_PATH_FUNC(func, filename) \
 -      const char *func(void) \
 -      { \
 -              static char *ret; \
 -              if (!ret) \
 -                      ret = git_pathdup(filename); \
 -              return ret; \
 -      }
 -
 -const char *git_path_cherry_pick_head(void);
 -const char *git_path_revert_head(void);
 -const char *git_path_squash_msg(void);
 -const char *git_path_merge_msg(void);
 -const char *git_path_merge_rr(void);
 -const char *git_path_merge_mode(void);
 -const char *git_path_merge_head(void);
 -const char *git_path_fetch_head(void);
 -const char *git_path_shallow(void);
 -
  /*
   * Return the name of the file in the local object database that would
   * be used to store a loose object with the specified sha1.  The
@@@ -921,8 -900,8 +921,8 @@@ extern char *sha1_pack_index_name(cons
   * The result will be at least `len` characters long, and will be NUL
   * terminated.
   *
 - * The non-`_r` version returns a static buffer which will be overwritten by
 - * subsequent calls.
 + * The non-`_r` version returns a static buffer which remains valid until 4
 + * more calls to find_unique_abbrev are made.
   *
   * The `_r` variant writes to a buffer supplied by the caller, which must be at
   * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
  extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
  extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
  
 -extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
 +extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
  extern const struct object_id null_oid;
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      int i;
 -
 -      for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
 -              if (*sha1 != *sha2)
 -                      return *sha1 - *sha2;
 -      }
 -
 -      return 0;
 +      return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
  }
  
  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@@ -967,13 -953,6 +967,13 @@@ static inline void oidcpy(struct object
        hashcpy(dst->hash, src->hash);
  }
  
 +static inline struct object_id *oiddup(const struct object_id *src)
 +{
 +      struct object_id *dst = xmalloc(sizeof(struct object_id));
 +      oidcpy(dst, src);
 +      return dst;
 +}
 +
  static inline void hashclr(unsigned char *hash)
  {
        memset(hash, 0, GIT_SHA1_RAWSZ);
@@@ -1022,6 -1001,9 +1022,6 @@@ static inline int is_empty_tree_oid(con
        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) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1046,9 -1028,8 +1046,9 @@@ int adjust_shared_perm(const char *path
  
  /*
   * Create the directory containing the named path, using care to be
 - * somewhat safe against races.  Return one of the scld_error values
 - * to indicate success/failure.
 + * somewhat safe against races. Return one of the scld_error values to
 + * indicate success/failure. On error, set errno to describe the
 + * problem.
   *
   * SCLD_VANISHED indicates that one of the ancestor directories of the
   * path existed at one point during the function call and then
@@@ -1072,64 -1053,17 +1072,64 @@@ enum scld_error 
  enum scld_error safe_create_leading_directories(char *path);
  enum scld_error safe_create_leading_directories_const(const char *path);
  
 +/*
 + * Callback function for raceproof_create_file(). This function is
 + * expected to do something that makes dirname(path) permanent despite
 + * the fact that other processes might be cleaning up empty
 + * directories at the same time. Usually it will create a file named
 + * path, but alternatively it could create another file in that
 + * directory, or even chdir() into that directory. The function should
 + * return 0 if the action was completed successfully. On error, it
 + * should return a nonzero result and set errno.
 + * raceproof_create_file() treats two errno values specially:
 + *
 + * - ENOENT -- dirname(path) does not exist. In this case,
 + *             raceproof_create_file() tries creating dirname(path)
 + *             (and any parent directories, if necessary) and calls
 + *             the function again.
 + *
 + * - EISDIR -- the file already exists and is a directory. In this
 + *             case, raceproof_create_file() removes the directory if
 + *             it is empty (and recursively any empty directories that
 + *             it contains) and calls the function again.
 + *
 + * Any other errno causes raceproof_create_file() to fail with the
 + * callback's return value and errno.
 + *
 + * Obviously, this function should be OK with being called again if it
 + * fails with ENOENT or EISDIR. In other scenarios it will not be
 + * called again.
 + */
 +typedef int create_file_fn(const char *path, void *cb);
 +
 +/*
 + * Create a file in dirname(path) by calling fn, creating leading
 + * directories if necessary. Retry a few times in case we are racing
 + * with another process that is trying to clean up the directory that
 + * contains path. See the documentation for create_file_fn for more
 + * details.
 + *
 + * Return the value and set the errno that resulted from the most
 + * recent call of fn. fn is always called at least once, and will be
 + * called more than once if it returns ENOENT or EISDIR.
 + */
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 +
  int mkdir_in_gitdir(const char *path);
 -extern char *expand_user_path(const char *path);
 +extern char *expand_user_path(const char *path, int real_home);
  const char *enter_repo(const char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
        return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 +char *strbuf_realpath(struct strbuf *resolved, const char *path,
 +                    int die_on_error);
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
 +char *real_pathdup(const char *path, int die_on_error);
  const char *absolute_path(const char *path);
 +char *absolute_pathdup(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
@@@ -1139,14 -1073,6 +1139,14 @@@ char *strip_path_suffix(const char *pat
  int daemon_avoid_alias(const char *path);
  extern int is_ntfs_dotgit(const char *name);
  
 +/*
 + * Returns true iff "str" could be confused as a command-line option when
 + * passed to a sub-program like "ssh". Note that this has nothing to do with
 + * shell-quoting, which should be handled separately; we're assuming here that
 + * the string makes it verbatim to the sub-program.
 + */
 +int looks_like_command_line_option(const char *str);
 +
  /**
   * Return a newly allocated string with the evaluation of
   * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
   */
  extern char *xdg_config_home(const char *filename);
  
 -/* object replacement */
 -#define LOOKUP_REPLACE_OBJECT 1
 -#define LOOKUP_UNKNOWN_OBJECT 2
 -extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
 +/**
 + * Return a newly allocated string with the evaluation of
 + * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
 + * "$HOME/.cache/git/$filename". Return NULL upon error.
 + */
 +extern char *xdg_cache_home(const char *filename);
 +
 +extern void *read_sha1_file_extended(const unsigned char *sha1,
 +                                   enum object_type *type,
 +                                   unsigned long *size, int lookup_replace);
  static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
  {
 -      return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
 +      return read_sha1_file_extended(sha1, type, size, 1);
  }
  
  /*
@@@ -1188,6 -1108,13 +1188,6 @@@ static inline const unsigned char *look
        return do_lookup_replace_object(sha1);
  }
  
 -static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
 -{
 -      if (!(flag & LOOKUP_REPLACE_OBJECT))
 -              return sha1;
 -      return lookup_replace_object(sha1);
 -}
 -
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
@@@ -1195,8 -1122,7 +1195,8 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 -extern int git_open_noatime(const char *name);
 +extern int git_open_cloexec(const char *name, int flags);
 +#define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -1211,23 -1137,15 +1211,23 @@@ extern int finalize_object_file(const c
  extern int has_sha1_pack(const unsigned char *sha1);
  
  /*
 - * Return true iff we have an object named sha1, whether local or in
 - * an alternate object database, and whether packed or loose.  This
 - * function does not respect replace references.
 + * Open the loose object at path, check its sha1, and return the contents,
 + * type, and size. If the object is a blob, then "contents" may return NULL,
 + * to allow streaming of large blobs.
   *
 - * If the QUICK flag is set, do not re-check the pack directory
 - * when we cannot find the object (this means we may give a false
 - * negative answer if another process is simultaneously repacking).
 + * Returns 0 on success, negative on error (details may be written to stderr).
 + */
 +int read_loose_object(const char *path,
 +                    const unsigned char *expected_sha1,
 +                    enum object_type *type,
 +                    unsigned long *size,
 +                    void **contents);
 +
 +/*
 + * Convenience for sha1_object_info_extended() with a NULL struct
 + * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass
 + * nonzero flags to also set other flags.
   */
 -#define HAS_SHA1_QUICK 0x1
  extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
  static inline int has_sha1_file(const unsigned char *sha1)
  {
  
  /* 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
@@@ -1249,9 -1166,6 +1249,9 @@@ extern int has_pack_index(const unsigne
  
  extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
  
 +/* Helper to check and "touch" a file */
 +extern int check_and_freshen_file(const char *fn, int freshen);
 +
  extern const signed char hexval_table[256];
  static inline unsigned int hexval(unsigned char c)
  {
@@@ -1272,55 -1186,40 +1272,55 @@@ static inline int hex2chr(const char *s
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
  
 +/* used when the code does not know or care what the default abbrev is */
 +#define FALLBACK_DEFAULT_ABBREV 7
 +
  struct object_context {
        unsigned char tree[20];
 -      char path[PATH_MAX];
        unsigned mode;
        /*
         * symlink_path is only used by get_tree_entry_follow_symlinks,
         * and only for symlinks that point outside the repository.
         */
        struct strbuf symlink_path;
 +      /*
 +       * If GET_OID_RECORD_PATH is set, this will record path (if any)
 +       * found when resolving the name. The caller is responsible for
 +       * releasing the memory.
 +       */
 +      char *path;
  };
  
 -#define GET_SHA1_QUIETLY           01
 -#define GET_SHA1_COMMIT            02
 -#define GET_SHA1_COMMITTISH        04
 -#define GET_SHA1_TREE             010
 -#define GET_SHA1_TREEISH          020
 -#define GET_SHA1_BLOB             040
 -#define GET_SHA1_FOLLOW_SYMLINKS 0100
 -#define GET_SHA1_ONLY_TO_DIE    04000
 -
 -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);
 -extern int get_sha1_tree(const char *str, unsigned char *sha1);
 -extern int get_sha1_treeish(const char *str, unsigned char *sha1);
 -extern int get_sha1_blob(const char *str, unsigned char *sha1);
 -extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
 +#define GET_OID_QUIETLY           01
 +#define GET_OID_COMMIT            02
 +#define GET_OID_COMMITTISH        04
 +#define GET_OID_TREE             010
 +#define GET_OID_TREEISH          020
 +#define GET_OID_BLOB             040
 +#define GET_OID_FOLLOW_SYMLINKS 0100
 +#define GET_OID_RECORD_PATH     0200
 +#define GET_OID_ONLY_TO_DIE    04000
 +
 +#define GET_OID_DISAMBIGUATORS \
 +      (GET_OID_COMMIT | GET_OID_COMMITTISH | \
 +      GET_OID_TREE | GET_OID_TREEISH | \
 +      GET_OID_BLOB)
  
  extern int get_oid(const char *str, struct object_id *oid);
 +extern int get_oid_commit(const char *str, struct object_id *oid);
 +extern int get_oid_committish(const char *str, struct object_id *oid);
 +extern int get_oid_tree(const char *str, struct object_id *oid);
 +extern int get_oid_treeish(const char *str, struct object_id *oid);
 +extern int get_oid_blob(const char *str, struct object_id *oid);
 +extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 +extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 +
  
 -typedef int each_abbrev_fn(const unsigned char *sha1, void *);
 +typedef int each_abbrev_fn(const struct object_id *oid, 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.
@@@ -1347,46 -1246,7 +1347,46 @@@ extern char *oid_to_hex_r(char *out, co
  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 *);
 +/*
 + * Parse a 40-character hexadecimal object ID starting from hex, updating the
 + * pointer specified by end when parsing stops.  The resulting object ID is
 + * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
 + * other invalid character.  end is only updated on success; otherwise, it is
 + * unmodified.
 + */
 +extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
 +
 +/*
 + * This reads short-hand syntax that not only evaluates to a commit
 + * object name, but also can act as if the end user spelled the name
 + * of the branch from the command line.
 + *
 + * - "@{-N}" finds the name of the Nth previous branch we were on, and
 + *   places the name of the branch in the given buf and returns the
 + *   number of characters parsed if successful.
 + *
 + * - "<branch>@{upstream}" finds the name of the other ref that
 + *   <branch> is configured to merge with (missing <branch> defaults
 + *   to the current branch), and places the name of the branch in the
 + *   given buf and returns the number of characters parsed if
 + *   successful.
 + *
 + * If the input is not of the accepted format, it returns a negative
 + * number to signal an error.
 + *
 + * If the input was ok but there are not N branch switches in the
 + * reflog, it returns 0.
 + *
 + * If "allowed" is non-zero, it is a treated as a bitfield of allowable
 + * expansions: local branches ("refs/heads/"), remote branches
 + * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
 + * allowed, even ones to refs outside of those namespaces.
 + */
 +#define INTERPRET_BRANCH_LOCAL (1<<0)
 +#define INTERPRET_BRANCH_REMOTE (1<<1)
 +#define INTERPRET_BRANCH_HEAD (1<<2)
 +extern int interpret_branch_name(const char *str, int len, struct strbuf *,
 +                               unsigned allowed);
  extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
@@@ -1428,18 -1288,18 +1428,18 @@@ struct date_mode 
  #define DATE_MODE(t) date_mode_from_type(DATE_##t)
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
 -const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
 -void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
 +void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
 -int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
 -int parse_expiry_date(const char *date, unsigned long *timestamp);
 +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
 +int parse_expiry_date(const char *date, timestamp_t *timestamp);
  void datestamp(struct strbuf *out);
  #define approxidate(s) approxidate_careful((s), NULL)
 -unsigned long approxidate_careful(const char *, int *);
 -unsigned long approxidate_relative(const char *date, const struct timeval *now);
 +timestamp_t approxidate_careful(const char *, int *);
 +timestamp_t approxidate_relative(const char *date, const struct timeval *now);
  void parse_date_format(const char *format, struct date_mode *mode);
 -int date_overflows(unsigned long date);
 +int date_overflows(timestamp_t date);
  
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
@@@ -1492,7 -1352,6 +1492,7 @@@ struct checkout 
        struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
 +      struct delayed_checkout *delayed_checkout;
        unsigned force:1,
                 quiet:1,
                 not_new:1,
  
  #define TEMPORARY_FILENAME_LENGTH 25
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 +extern void enable_delayed_checkout(struct checkout *state);
 +extern int finish_delayed_checkout(struct checkout *state);
  
  struct cache_def {
        struct strbuf path;
@@@ -1526,35 -1383,14 +1526,34 @@@ 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;
 +
 +      /*
 +       * Used to store the results of readdir(3) calls when searching
 +       * for unique abbreviated hashes.  This cache is never
 +       * invalidated, thus it's racy and not necessarily accurate.
 +       * That's fine for its purpose; don't use it for tasks requiring
 +       * greater accuracy!
 +       */
 +      char loose_objects_subdir_seen[256];
 +      struct oid_array loose_objects_cache;
 +
 +      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 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.
@@@ -1568,14 -1404,6 +1567,14 @@@ extern void add_to_alternates_file(cons
   */
  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;
@@@ -1632,41 -1460,11 +1631,41 @@@ extern void prepare_packed_git(void)
  extern void reprepare_packed_git(void);
  extern void install_packed_git(struct packed_git *pack);
  
 +/*
 + * Give a rough count of objects in the repository. This sacrifices accuracy
 + * for speed.
 + */
 +unsigned long approximate_object_count(void);
 +
  extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                         struct packed_git *packs);
  
  extern void pack_report(void);
  
 +/*
 + * Create a temporary file rooted in the object database directory, or
 + * die on failure. The filename is taken from "pattern", which should have the
 + * usual "XXXXXX" trailer, and the resulting filename is written into the
 + * "template" buffer. Returns the open descriptor.
 + */
 +extern int odb_mkstemp(struct strbuf *template, const char *pattern);
 +
 +/*
 + * Generate the filename to be used for a pack file with checksum "sha1" and
 + * extension "ext". The result is written into the strbuf "buf", overwriting
 + * any existing contents. A pointer to buf->buf is returned as a convenience.
 + *
 + * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
 + */
 +extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 +
 +/*
 + * Create a pack .keep file named "name" (which should generally be the output
 + * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
 + * error.
 + */
 +extern int odb_pack_keep(const char *name);
 +
  /*
   * mmap the index file for the specified packfile (if it is not
   * already mmapped).  Return 0 on success.
@@@ -1703,12 -1501,6 +1702,12 @@@ extern void check_pack_index_ptr(const 
   * error.
   */
  extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 +/*
 + * Like nth_packed_object_sha1, but write the data into the object specified by
 + * the the first argument.  Returns the first argument on success, and NULL on
 + * error.
 + */
 +extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
  
  /*
   * Return the offset of the nth object within the specified packfile.
@@@ -1750,21 -1542,15 +1749,21 @@@ extern int unpack_object_header(struct 
   * scratch buffer, but restored to its original contents before
   * the function returns.
   */
 -typedef int each_loose_object_fn(const unsigned char *sha1,
 +typedef int each_loose_object_fn(const struct object_id *oid,
                                 const char *path,
                                 void *data);
  typedef int each_loose_cruft_fn(const char *basename,
                                const char *path,
                                void *data);
 -typedef int each_loose_subdir_fn(int nr,
 +typedef int each_loose_subdir_fn(unsigned int nr,
                                 const char *path,
                                 void *data);
 +int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 +                              struct strbuf *path,
 +                              each_loose_object_fn obj_cb,
 +                              each_loose_cruft_fn cruft_cb,
 +                              each_loose_subdir_fn subdir_cb,
 +                              void *data);
  int for_each_loose_file_in_objdir(const char *path,
                                  each_loose_object_fn obj_cb,
                                  each_loose_cruft_fn cruft_cb,
@@@ -1782,7 -1568,7 +1781,7 @@@ int for_each_loose_file_in_objdir_buf(s
   * LOCAL_ONLY flag is set).
   */
  #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -typedef int each_packed_object_fn(const unsigned char *sha1,
 +typedef int each_packed_object_fn(const struct object_id *oid,
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
@@@ -1796,7 -1582,6 +1795,7 @@@ struct object_info 
        off_t *disk_sizep;
        unsigned char *delta_base_sha1;
        struct strbuf *typename;
 +      void **contentp;
  
        /* Response */
        enum {
                } 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}
 +
 +/* Invoke lookup_replace_object() on the given hash */
 +#define OBJECT_INFO_LOOKUP_REPLACE 1
 +/* Allow reading from a loose object file of unknown/bogus type */
 +#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
 +/* Do not check cached storage */
 +#define OBJECT_INFO_SKIP_CACHED 4
 +/* Do not retry packed storage after checking packed and loose storage */
 +#define OBJECT_INFO_QUICK 8
  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);
  
 -/* git_config_parse_key() returns these negated: */
 -#define CONFIG_INVALID_KEY 1
 -#define CONFIG_NO_SECTION_OR_NAME 2
 -/* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */
 -#define CONFIG_NO_LOCK -1
 -#define CONFIG_INVALID_FILE 3
 -#define CONFIG_NO_WRITE 4
 -#define CONFIG_NOTHING_SET 5
 -#define CONFIG_INVALID_PATTERN 6
 -#define CONFIG_GENERIC_ERROR 7
 -
 -#define CONFIG_REGEX_NONE ((void *)1)
 -
 -struct git_config_source {
 -      unsigned int use_stdin:1;
 -      const char *file;
 -      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 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);
 -extern void git_config(config_fn_t fn, void *);
 -extern int git_config_with_options(config_fn_t fn, void *,
 -                                 struct git_config_source *config_source,
 -                                 int respect_includes);
 -extern int git_parse_ulong(const char *, unsigned long *);
 -extern int git_parse_maybe_bool(const char *);
 -extern int git_config_int(const char *, const char *);
 -extern int64_t git_config_int64(const char *, const char *);
 -extern unsigned long git_config_ulong(const char *, const char *);
 -extern int git_config_bool_or_int(const char *, const char *, int *);
 -extern int git_config_bool(const char *, const char *);
 -extern int git_config_maybe_bool(const char *, const char *);
 -extern int git_config_string(const char **, const char *, const char *);
 -extern int git_config_pathname(const char **, const char *, const char *);
 -extern int git_config_set_in_file_gently(const char *, const char *, const char *);
 -extern void git_config_set_in_file(const char *, const char *, const char *);
 -extern int git_config_set_gently(const char *, const char *);
 -extern void git_config_set(const char *, const char *);
 -extern int git_config_parse_key(const char *, char **, int *);
 -extern int git_config_key_is_valid(const char *key);
 -extern int git_config_set_multivar_gently(const char *, const char *, const char *, int);
 -extern void git_config_set_multivar(const char *, const char *, const char *, int);
 -extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
 -extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
 -extern int git_config_rename_section(const char *, const char *);
 -extern int git_config_rename_section_in_file(const char *, const char *, const char *);
 -extern const char *git_etc_gitconfig(void);
 -extern int git_env_bool(const char *, int);
 -extern unsigned long git_env_ulong(const char *, unsigned long);
 -extern int git_config_system(void);
 -extern int config_error_nonbool(const char *);
 -#if defined(__GNUC__)
 -#define config_error_nonbool(s) (config_error_nonbool(s), const_error())
 -#endif
  extern const char *get_log_output_encoding(void);
  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);
 -
 -struct config_include_data {
 -      int depth;
 -      config_fn_t fn;
 -      void *data;
 -};
 -#define CONFIG_INCLUDE_INIT { 0 }
 -extern int git_config_include(const char *name, const char *value, void *data);
 -
 -/*
 - * Match and parse a config key of the form:
 - *
 - *   section.(subsection.)?key
 - *
 - * (i.e., what gets handed to a config_fn_t). The caller provides the section;
 - * we return -1 if it does not match, 0 otherwise. The subsection and key
 - * out-parameters are filled by the function (and subsection is NULL if it is
 - * missing).
 - */
 -extern int parse_config_key(const char *var,
 -                          const char *section,
 -                          const char **subsection, int *subsection_len,
 -                          const char **key);
 -
 -struct config_set_element {
 -      struct hashmap_entry ent;
 -      char *key;
 -      struct string_list value_list;
 -};
 -
 -struct configset_list_item {
 -      struct config_set_element *e;
 -      int value_index;
 -};
 -
 -/*
 - * the contents of the list are ordered according to their
 - * position in the config files and order of parsing the files.
 - * (i.e. key-value pair at the last position of .git/config will
 - * be at the last item of the list)
 - */
 -struct configset_list {
 -      struct configset_list_item *items;
 -      unsigned int nr, alloc;
 -};
 -
 -struct config_set {
 -      struct hashmap config_hash;
 -      int hash_initialized;
 -      struct configset_list list;
 -};
 -
 -extern void git_configset_init(struct config_set *cs);
 -extern int git_configset_add_file(struct config_set *cs, const char *filename);
 -extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value);
 -extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
 -extern void git_configset_clear(struct config_set *cs);
 -extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
 -extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 -extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
 -extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
 -extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
 -extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
 -extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
 -extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
 -
 -extern int git_config_get_value(const char *key, const char **value);
 -extern const struct string_list *git_config_get_value_multi(const char *key);
 -extern void git_config_clear(void);
 -extern void git_config_iter(config_fn_t fn, void *data);
 -extern int git_config_get_string_const(const char *key, const char **dest);
 -extern int git_config_get_string(const char *key, char **dest);
 -extern int git_config_get_int(const char *key, int *dest);
 -extern int git_config_get_ulong(const char *key, unsigned long *dest);
 -extern int git_config_get_bool(const char *key, int *dest);
 -extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
 -extern int git_config_get_maybe_bool(const char *key, int *dest);
 -extern int git_config_get_pathname(const char *key, const char **dest);
 -extern int git_config_get_untracked_cache(void);
 -
  /*
   * This is a hack for test programs like test-dump-untracked-cache to
   * ensure that they do not modify the untracked cache when reading it.
   */
  extern int ignore_untracked_cache_config;
  
 -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)));
 -extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 -
  extern int committer_ident_sufficiently_given(void);
  extern int author_ident_sufficiently_given(void);
  
@@@ -1967,8 -1907,7 +1966,8 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 +void overlay_tree_on_index(struct index_state *istate,
 +                         const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
  int split_cmdline(char *cmdline, const char ***argv);
@@@ -1987,8 -1926,8 +1986,8 @@@ struct commit_list
  int try_merge_command(const char *strategy, size_t xopts_nr,
                const char **xopts, struct commit_list *common,
                const char *head_arg, struct commit_list *remotes);
 -int checkout_fast_forward(const unsigned char *from,
 -                        const unsigned char *to,
 +int checkout_fast_forward(const struct object_id *from,
 +                        const struct object_id *to,
                          int overwrite_ignore);
  
  
diff --combined sha1_file.c
index 79feb1a305e454f361e9707121d72267f741dbf9,fac860ad9bbcc79866f0dd1bb8100af77b6d5ab9..189a1c3cdd14ad7a9d7e72a4577f47449e2ed5f6
@@@ -7,7 -7,6 +7,7 @@@
   * creation etc.
   */
  #include "cache.h"
 +#include "config.h"
  #include "string-list.h"
  #include "lockfile.h"
  #include "delta.h"
  #include "mru.h"
  #include "list.h"
  #include "mergesort.h"
 -
 -#ifndef O_NOATIME
 -#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
 -#define O_NOATIME 01000000
 -#else
 -#define O_NOATIME 0
 -#endif
 -#endif
 +#include "quote.h"
  
  #define SZ_FMT PRIuMAX
  static inline uintmax_t sz_fmt(size_t s) { return s; }
@@@ -130,10 -136,8 +130,10 @@@ enum scld_error safe_create_leading_dir
                *slash = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
 -                      if (!S_ISDIR(st.st_mode))
 +                      if (!S_ISDIR(st.st_mode)) {
 +                              errno = ENOTDIR;
                                ret = SCLD_EXISTS;
 +                      }
                } else if (mkdir(path, 0777)) {
                        if (errno == EEXIST &&
                            !stat(path, &st) && S_ISDIR(st.st_mode))
  
  enum scld_error safe_create_leading_directories_const(const char *path)
  {
 +      int save_errno;
        /* path points to cache entries, so xstrdup before messing with it */
        char *buf = xstrdup(path);
        enum scld_error result = safe_create_leading_directories(buf);
 +
 +      save_errno = errno;
        free(buf);
 +      errno = save_errno;
        return result;
  }
  
 -static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
 +{
 +      /*
 +       * The number of times we will try to remove empty directories
 +       * in the way of path. This is only 1 because if another
 +       * process is racily creating directories that conflict with
 +       * us, we don't want to fight against them.
 +       */
 +      int remove_directories_remaining = 1;
 +
 +      /*
 +       * The number of times that we will try to create the
 +       * directories containing path. We are willing to attempt this
 +       * more than once, because another process could be trying to
 +       * clean up empty directories at the same time as we are
 +       * trying to create them.
 +       */
 +      int create_directories_remaining = 3;
 +
 +      /* A scratch copy of path, filled lazily if we need it: */
 +      struct strbuf path_copy = STRBUF_INIT;
 +
 +      int ret, save_errno;
 +
 +      /* Sanity check: */
 +      assert(*path);
 +
 +retry_fn:
 +      ret = fn(path, cb);
 +      save_errno = errno;
 +      if (!ret)
 +              goto out;
 +
 +      if (errno == EISDIR && remove_directories_remaining-- > 0) {
 +              /*
 +               * A directory is in the way. Maybe it is empty; try
 +               * to remove it:
 +               */
 +              if (!path_copy.len)
 +                      strbuf_addstr(&path_copy, path);
 +
 +              if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
 +                      goto retry_fn;
 +      } else if (errno == ENOENT && create_directories_remaining-- > 0) {
 +              /*
 +               * Maybe the containing directory didn't exist, or
 +               * maybe it was just deleted by a process that is
 +               * racing with us to clean up empty directories. Try
 +               * to create it:
 +               */
 +              enum scld_error scld_result;
 +
 +              if (!path_copy.len)
 +                      strbuf_addstr(&path_copy, path);
 +
 +              do {
 +                      scld_result = safe_create_leading_directories(path_copy.buf);
 +                      if (scld_result == SCLD_OK)
 +                              goto retry_fn;
 +              } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
 +      }
 +
 +out:
 +      strbuf_release(&path_copy);
 +      errno = save_errno;
 +      return ret;
 +}
 +
 +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;
  }
  
 -/*
 - * Return the name of the pack or index file with the specified sha1
 - * in its filename.  *base and *name are scratch space that must be
 - * provided by the caller.  which should be "pack" or "idx".
 - */
 -static char *sha1_get_pack_name(const unsigned char *sha1,
 -                              struct strbuf *buf,
 -                              const char *which)
 +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;
 +}
 +
 + char *odb_pack_name(struct strbuf *buf,
 +                   const unsigned char *sha1,
 +                   const char *ext)
  {
        strbuf_reset(buf);
        strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
 -                  sha1_to_hex(sha1), which);
 +                  sha1_to_hex(sha1), ext);
        return buf->buf;
  }
  
  char *sha1_pack_name(const unsigned char *sha1)
  {
        static struct strbuf buf = STRBUF_INIT;
 -      return sha1_get_pack_name(sha1, &buf, "pack");
 +      return odb_pack_name(&buf, sha1, "pack");
  }
  
  char *sha1_pack_index_name(const unsigned char *sha1)
  {
        static struct strbuf buf = STRBUF_INIT;
 -      return sha1_get_pack_name(sha1, &buf, "idx");
 +      return odb_pack_name(&buf, sha1, "idx");
  }
  
  struct alternate_object_database *alt_odb_list;
@@@ -323,7 -254,8 +323,7 @@@ static int alt_odb_usable(struct strbu
         * thing twice, or object directory itself.
         */
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              if (path->len == alt->name - alt->base - 1 &&
 -                  !memcmp(path->buf, alt->base, path->len))
 +              if (!fspathcmp(path->buf, alt->path))
                        return 0;
        }
        if (!fspathcmp(path->buf, normalized_objdir))
   * SHA1, an extra slash for the first level indirection, and the
   * terminating NUL.
   */
+ static void read_info_alternates(const char * relative_base, int depth);
  static int link_alt_odb_entry(const char *entry, const char *relative_base,
        int depth, const char *normalized_objdir)
  {
        struct alternate_object_database *ent;
 -      size_t entlen;
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
 -              strbuf_addstr(&pathbuf, real_path(relative_base));
 +              strbuf_realpath(&pathbuf, relative_base, 1);
                strbuf_addch(&pathbuf, '/');
        }
        strbuf_addstr(&pathbuf, entry);
  
 -      if (strbuf_normalize_path(&pathbuf) < 0) {
 +      if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) {
                error("unable to normalize alternate object path: %s",
                      pathbuf.buf);
                strbuf_release(&pathbuf);
                return -1;
        }
  
 -      entlen = st_add(pathbuf.len, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */
 -      ent = xmalloc(st_add(sizeof(*ent), entlen));
 -      memcpy(ent->base, pathbuf.buf, pathbuf.len);
 -
 -      ent->name = ent->base + pathbuf.len + 1;
 -      ent->base[pathbuf.len] = '/';
 -      ent->base[pathbuf.len + 3] = '/';
 -      ent->base[entlen-1] = 0;
 +      ent = alloc_alt_odb(pathbuf.buf);
  
        /* add the alternate entry */
        *alt_odb_tail = ent;
        return 0;
  }
  
 +static const char *parse_alt_odb_entry(const char *string,
 +                                     int sep,
 +                                     struct strbuf *out)
 +{
 +      const char *end;
 +
 +      strbuf_reset(out);
 +
 +      if (*string == '#') {
 +              /* comment; consume up to next separator */
 +              end = strchrnul(string, sep);
 +      } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
 +              /*
 +               * quoted path; unquote_c_style has copied the
 +               * data for us and set "end". Broken quoting (e.g.,
 +               * an entry that doesn't end with a quote) falls
 +               * back to the unquoted case below.
 +               */
 +      } else {
 +              /* normal, unquoted path */
 +              end = strchrnul(string, sep);
 +              strbuf_add(out, string, end - string);
 +      }
 +
 +      if (*end)
 +              end++;
 +      return end;
 +}
 +
  static void link_alt_odb_entries(const char *alt, int len, int sep,
                                 const char *relative_base, int depth)
  {
 -      struct string_list entries = STRING_LIST_INIT_NODUP;
 -      char *alt_copy;
 -      int i;
        struct strbuf objdirbuf = STRBUF_INIT;
 +      struct strbuf entry = STRBUF_INIT;
  
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                die("unable to normalize object directory: %s",
                    objdirbuf.buf);
  
 -      alt_copy = xmemdupz(alt, len);
 -      string_list_split_in_place(&entries, alt_copy, sep, -1);
 -      for (i = 0; i < entries.nr; i++) {
 -              const char *entry = entries.items[i].string;
 -              if (entry[0] == '\0' || entry[0] == '#')
 +      while (*alt) {
 +              alt = parse_alt_odb_entry(alt, sep, &entry);
 +              if (!entry.len)
                        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.buf, relative_base, depth, objdirbuf.buf);
        }
 -      string_list_clear(&entries, 0);
 -      free(alt_copy);
 +      strbuf_release(&entry);
        strbuf_release(&objdirbuf);
  }
  
- void read_info_alternates(const char * relative_base, int depth)
static void read_info_alternates(const char * relative_base, int depth)
  {
        char *map;
        size_t mapsz;
        int fd;
  
        path = xstrfmt("%s/info/alternates", relative_base);
 -      fd = git_open_noatime(path);
 +      fd = git_open(path);
        free(path);
        if (fd < 0)
                return;
        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));
@@@ -611,7 -521,8 +612,7 @@@ char *compute_alternate_path(const cha
  
  out:
        if (seen_error) {
 -              free(ref_git);
 -              ref_git = NULL;
 +              FREE_AND_NULL(ref_git);
        }
  
        return ref_git;
@@@ -662,7 -573,7 +663,7 @@@ static int freshen_file(const char *fn
   * either does not exist on disk, or has a stale mtime and may be subject to
   * pruning).
   */
 -static int check_and_freshen_file(const char *fn, int freshen)
 +int check_and_freshen_file(const char *fn, int freshen)
  {
        if (access(fn, F_OK))
                return 0;
@@@ -681,8 -592,8 +682,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;
@@@ -749,7 -660,7 +750,7 @@@ static int check_packed_git_idx(const c
        struct pack_idx_header *hdr;
        size_t idx_size;
        uint32_t version, nr, i, *index;
 -      int fd = git_open_noatime(path);
 +      int fd = git_open(path);
        struct stat st;
  
        if (fd < 0)
@@@ -1155,7 -1066,7 +1156,7 @@@ static int open_packed_git_1(struct pac
        while (pack_max_fds <= pack_open_fds && close_one_pack())
                ; /* nothing */
  
 -      p->pack_fd = git_open_noatime(p->pack_name);
 +      p->pack_fd = git_open(p->pack_name);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
        pack_open_fds++;
@@@ -1496,32 -1407,6 +1497,32 @@@ static void prepare_packed_git_one(cha
        strbuf_release(&path);
  }
  
 +static int approximate_object_count_valid;
 +
 +/*
 + * Give a fast, rough count of the number of objects in the repository. This
 + * ignores loose objects completely. If you have a lot of them, then either
 + * you should repack because your performance will be awful, or they are
 + * all unreachable objects about to be pruned, in which case they're not really
 + * interesting as a measure of repo size in the first place.
 + */
 +unsigned long approximate_object_count(void)
 +{
 +      static unsigned long count;
 +      if (!approximate_object_count_valid) {
 +              struct packed_git *p;
 +
 +              prepare_packed_git();
 +              count = 0;
 +              for (p = packed_git; p; p = p->next) {
 +                      if (open_pack_index(p))
 +                              continue;
 +                      count += p->num_objects;
 +              }
 +      }
 +      return count;
 +}
 +
  static void *get_next_packed_git(const void *p)
  {
        return ((const struct packed_git *)p)->next;
@@@ -1584,8 -1469,11 +1585,8 @@@ 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;
  
  void reprepare_packed_git(void)
  {
 +      approximate_object_count_valid = 0;
        prepare_packed_git_run_once = 0;
        prepare_packed_git();
  }
@@@ -1606,7 -1493,7 +1607,7 @@@ static void mark_bad_packed_object(stru
                if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
 -                                    st_mult(GIT_SHA1_RAWSZ,
 +                                    st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
        hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
        p->num_bad_objects++;
@@@ -1672,81 -1559,61 +1673,81 @@@ int check_sha1_signature(const unsigne
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
 -int git_open_noatime(const char *name)
 +int git_open_cloexec(const char *name, int flags)
  {
 -      static int sha1_file_open_flag = O_NOATIME;
 +      int fd;
 +      static int o_cloexec = O_CLOEXEC;
  
 -      for (;;) {
 -              int fd;
 +      fd = open(name, flags | o_cloexec);
 +      if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
 +              /* Try again w/o O_CLOEXEC: the kernel might not support it */
 +              o_cloexec &= ~O_CLOEXEC;
 +              fd = open(name, flags | o_cloexec);
 +      }
  
 -              errno = 0;
 -              fd = open(name, O_RDONLY | sha1_file_open_flag);
 -              if (fd >= 0)
 -                      return fd;
 +#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
 +      {
 +              static int fd_cloexec = FD_CLOEXEC;
  
 -              /* Might the failure be due to O_NOATIME? */
 -              if (errno != ENOENT && sha1_file_open_flag) {
 -                      sha1_file_open_flag = 0;
 -                      continue;
 +              if (!o_cloexec && 0 <= fd && fd_cloexec) {
 +                      /* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */
 +                      int flags = fcntl(fd, F_GETFD);
 +                      if (fcntl(fd, F_SETFD, flags | fd_cloexec))
 +                              fd_cloexec = 0;
                }
 -
 -              return -1;
        }
 +#endif
 +      return fd;
  }
  
 -static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
 +/*
 + * Find "sha1" as a loose object in the local repository or in an alternate.
 + * Returns 0 on success, negative on failure.
 + *
 + * The "path" out-parameter will give the path of the object we found (if any).
 + * Note that it may point to static storage and is only valid until another
 + * call to sha1_file_name(), etc.
 + */
 +static int stat_sha1_file(const unsigned char *sha1, struct stat *st,
 +                        const char **path)
  {
        struct alternate_object_database *alt;
  
 -      if (!lstat(sha1_file_name(sha1), st))
 +      *path = sha1_file_name(sha1);
 +      if (!lstat(*path, st))
                return 0;
  
        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))
 +              *path = alt_sha1_path(alt, sha1);
 +              if (!lstat(*path, st))
                        return 0;
        }
  
        return -1;
  }
  
 -static int open_sha1_file(const unsigned char *sha1)
 +/*
 + * Like stat_sha1_file(), but actually open the object and return the
 + * descriptor. See the caveats on the "path" parameter above.
 + */
 +static int open_sha1_file(const unsigned char *sha1, const char **path)
  {
        int fd;
        struct alternate_object_database *alt;
        int most_interesting_errno;
  
 -      fd = git_open_noatime(sha1_file_name(sha1));
 +      *path = sha1_file_name(sha1);
 +      fd = git_open(*path);
        if (fd >= 0)
                return fd;
        most_interesting_errno = errno;
  
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              fill_sha1_path(alt->name, sha1);
 -              fd = git_open_noatime(alt->base);
 +              *path = alt_sha1_path(alt, sha1);
 +              fd = git_open(*path);
                if (fd >= 0)
                        return fd;
                if (most_interesting_errno == ENOENT)
        return -1;
  }
  
 -void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 +/*
 + * Map the loose object at "path" if it is not NULL, or the path found by
 + * searching for a loose object named "sha1".
 + */
 +static void *map_sha1_file_1(const char *path,
 +                           const unsigned char *sha1,
 +                           unsigned long *size)
  {
        void *map;
        int fd;
  
 -      fd = open_sha1_file(sha1);
 +      if (path)
 +              fd = git_open(path);
 +      else
 +              fd = open_sha1_file(sha1, &path);
        map = NULL;
        if (fd >= 0) {
                struct stat st;
                        *size = xsize_t(st.st_size);
                        if (!*size) {
                                /* mmap() is forbidden on empty files */
 -                              error("object file %s is empty", sha1_file_name(sha1));
 +                              error("object file %s is empty", path);
                                return NULL;
                        }
                        map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
        return map;
  }
  
 +void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 +{
 +      return map_sha1_file_1(NULL, sha1, size);
 +}
 +
  unsigned long unpack_object_header_buffer(const unsigned char *buf,
                unsigned long len, enum object_type *type, unsigned long *sizep)
  {
@@@ -1964,7 -1817,7 +1965,7 @@@ static int parse_sha1_header_extended(c
         * we're obtaining the type using '--allow-unknown-type'
         * option.
         */
 -      if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
 +      if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0))
                type = 0;
        else if (type < 0)
                die("invalid object type");
  
  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);
 -}
 -
 -static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
 -{
 -      int ret;
 -      git_zstream stream;
 -      char hdr[8192];
 -
 -      ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
 -      if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
 -              return NULL;
 -
 -      return unpack_sha1_rest(&stream, hdr, *size, sha1);
 +      return parse_sha1_header_extended(hdr, &oi, 0);
  }
  
  unsigned long get_size_from_delta(struct packed_git *p,
@@@ -2226,6 -2094,107 +2227,6 @@@ unwind
        goto out;
  }
  
 -static 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;
 -      off_t curpos = obj_offset;
 -      enum object_type type;
 -
 -      /*
 -       * We always get the representation type, but only convert it to
 -       * a "real" type later if the caller is interested.
 -       */
 -      type = unpack_object_header(p, &w_curs, &curpos, &size);
 -
 -      if (oi->sizep) {
 -              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 -                      off_t tmp_pos = curpos;
 -                      off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
 -                                                         type, obj_offset);
 -                      if (!base_offset) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -                      *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
 -                      if (*oi->sizep == 0) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -              } else {
 -                      *oi->sizep = size;
 -              }
 -      }
 -
 -      if (oi->disk_sizep) {
 -              struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 -              *oi->disk_sizep = revidx[1].offset - obj_offset;
 -      }
 -
 -      if (oi->typep) {
 -              *oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos);
 -              if (*oi->typep < 0) {
 -                      type = OBJ_BAD;
 -                      goto out;
 -              }
 -      }
 -
 -      if (oi->delta_base_sha1) {
 -              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 -                      const unsigned char *base;
 -
 -                      base = get_delta_base_sha1(p, &w_curs, curpos,
 -                                                 type, obj_offset);
 -                      if (!base) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -
 -                      hashcpy(oi->delta_base_sha1, base);
 -              } else
 -                      hashclr(oi->delta_base_sha1);
 -      }
 -
 -out:
 -      unuse_pack(&w_curs);
 -      return type;
 -}
 -
 -static void *unpack_compressed_entry(struct packed_git *p,
 -                                  struct pack_window **w_curs,
 -                                  off_t curpos,
 -                                  unsigned long size)
 -{
 -      int st;
 -      git_zstream stream;
 -      unsigned char *buffer, *in;
 -
 -      buffer = xmallocz_gently(size);
 -      if (!buffer)
 -              return NULL;
 -      memset(&stream, 0, sizeof(stream));
 -      stream.next_out = buffer;
 -      stream.avail_out = size + 1;
 -
 -      git_inflate_init(&stream);
 -      do {
 -              in = use_pack(p, w_curs, curpos, &stream.avail_in);
 -              stream.next_in = in;
 -              st = git_inflate(&stream, Z_FINISH);
 -              if (!stream.avail_out)
 -                      break; /* the payload is larger than it should be */
 -              curpos += stream.next_in - in;
 -      } while (st == Z_OK || st == Z_BUF_ERROR);
 -      git_inflate_end(&stream);
 -      if ((st != Z_STREAM_END) || stream.total_out != size) {
 -              free(buffer);
 -              return NULL;
 -      }
 -
 -      return buffer;
 -}
 -
  static struct hashmap delta_base_cache;
  static size_t delta_base_cached;
  
@@@ -2275,8 -2244,7 +2276,8 @@@ static int delta_base_cache_key_eq(cons
        return a->p == b->p && a->base_offset == b->base_offset;
  }
  
 -static int delta_base_cache_hash_cmp(const void *va, const void *vb,
 +static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
 +                                   const void *va, const void *vb,
                                     const void *vkey)
  {
        const struct delta_base_cache_entry *a = va, *b = vb;
@@@ -2314,10 -2282,8 +2315,10 @@@ static void *cache_or_unpack_entry(stru
        if (!ent)
                return unpack_entry(p, base_offset, type, base_size);
  
 -      *type = ent->type;
 -      *base_size = ent->size;
 +      if (type)
 +              *type = ent->type;
 +      if (base_size)
 +              *base_size = ent->size;
        return xmemdupz(ent->data, ent->size);
  }
  
@@@ -2329,10 -2295,11 +2330,10 @@@ static inline void release_delta_base_c
  
  void clear_delta_base_cache(void)
  {
 -      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)) {
 +      struct list_head *lru, *tmp;
 +      list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
 +              struct delta_base_cache_entry *entry =
 +                      list_entry(lru, struct delta_base_cache_entry, lru);
                release_delta_base_cache(entry);
        }
  }
@@@ -2361,131 -2328,11 +2362,131 @@@ static void add_delta_base_cache(struc
        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_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
        hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
        hashmap_add(&delta_base_cache, ent);
  }
  
 +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;
 +      off_t curpos = obj_offset;
 +      enum object_type type;
 +
 +      /*
 +       * We always get the representation type, but only convert it to
 +       * a "real" type later if the caller is interested.
 +       */
 +      if (oi->contentp) {
 +              *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep,
 +                                                    &type);
 +              if (!*oi->contentp)
 +                      type = OBJ_BAD;
 +      } else {
 +              type = unpack_object_header(p, &w_curs, &curpos, &size);
 +      }
 +
 +      if (!oi->contentp && oi->sizep) {
 +              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 +                      off_t tmp_pos = curpos;
 +                      off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
 +                                                         type, obj_offset);
 +                      if (!base_offset) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +                      *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
 +                      if (*oi->sizep == 0) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +              } else {
 +                      *oi->sizep = size;
 +              }
 +      }
 +
 +      if (oi->disk_sizep) {
 +              struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 +              *oi->disk_sizep = revidx[1].offset - obj_offset;
 +      }
 +
 +      if (oi->typep || oi->typename) {
 +              enum object_type ptot;
 +              ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
 +                                           curpos);
 +              if (oi->typep)
 +                      *oi->typep = ptot;
 +              if (oi->typename) {
 +                      const char *tn = typename(ptot);
 +                      if (tn)
 +                              strbuf_addstr(oi->typename, tn);
 +              }
 +              if (ptot < 0) {
 +                      type = OBJ_BAD;
 +                      goto out;
 +              }
 +      }
 +
 +      if (oi->delta_base_sha1) {
 +              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 +                      const unsigned char *base;
 +
 +                      base = get_delta_base_sha1(p, &w_curs, curpos,
 +                                                 type, obj_offset);
 +                      if (!base) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +
 +                      hashcpy(oi->delta_base_sha1, base);
 +              } else
 +                      hashclr(oi->delta_base_sha1);
 +      }
 +
 +      oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
 +                                                        OI_PACKED;
 +
 +out:
 +      unuse_pack(&w_curs);
 +      return type;
 +}
 +
 +static void *unpack_compressed_entry(struct packed_git *p,
 +                                  struct pack_window **w_curs,
 +                                  off_t curpos,
 +                                  unsigned long size)
 +{
 +      int st;
 +      git_zstream stream;
 +      unsigned char *buffer, *in;
 +
 +      buffer = xmallocz_gently(size);
 +      if (!buffer)
 +              return NULL;
 +      memset(&stream, 0, sizeof(stream));
 +      stream.next_out = buffer;
 +      stream.avail_out = size + 1;
 +
 +      git_inflate_init(&stream);
 +      do {
 +              in = use_pack(p, w_curs, curpos, &stream.avail_in);
 +              stream.next_in = in;
 +              st = git_inflate(&stream, Z_FINISH);
 +              if (!stream.avail_out)
 +                      break; /* the payload is larger than it should be */
 +              curpos += stream.next_in - in;
 +      } while (st == Z_OK || st == Z_BUF_ERROR);
 +      git_inflate_end(&stream);
 +      if ((st != Z_STREAM_END) || stream.total_out != size) {
 +              free(buffer);
 +              return NULL;
 +      }
 +
 +      return buffer;
 +}
 +
  static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size);
  
@@@ -2545,8 -2392,8 +2546,8 @@@ void *unpack_entry(struct packed_git *p
                                error("bad packed object CRC for %s",
                                      sha1_to_hex(sha1));
                                mark_bad_packed_object(p, sha1);
 -                              unuse_pack(&w_curs);
 -                              return NULL;
 +                              data = NULL;
 +                              goto out;
                        }
                }
  
        while (delta_stack_nr) {
                void *delta_data;
                void *base = data;
 +              void *external_base = NULL;
                unsigned long delta_size, base_size = size;
                int i;
  
                                      p->pack_name);
                                mark_bad_packed_object(p, base_sha1);
                                base = read_object(base_sha1, &type, &base_size);
 +                              external_base = base;
                        }
                }
  
                              "at offset %"PRIuMAX" from %s",
                              (uintmax_t)curpos, p->pack_name);
                        data = NULL;
 +                      free(external_base);
                        continue;
                }
  
                        error("failed to apply delta");
  
                free(delta_data);
 +              free(external_base);
        }
  
 -      *final_type = type;
 -      *final_size = size;
 +      if (final_type)
 +              *final_type = type;
 +      if (final_size)
 +              *final_size = size;
  
 +out:
        unuse_pack(&w_curs);
  
        if (delta_stack != small_delta_stack)
@@@ -2713,17 -2553,6 +2714,17 @@@ const unsigned char *nth_packed_object_
        }
  }
  
 +const struct object_id *nth_packed_object_oid(struct object_id *oid,
 +                                            struct packed_git *p,
 +                                            uint32_t n)
 +{
 +      const unsigned char *hash = nth_packed_object_sha1(p, n);
 +      if (!hash)
 +              return NULL;
 +      hashcpy(oid->hash, hash);
 +      return oid;
 +}
 +
  void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
  {
        const unsigned char *ptr = vptr;
@@@ -2763,6 -2592,7 +2764,6 @@@ off_t find_pack_entry_one(const unsigne
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
        unsigned hi, lo, stride;
 -      static int use_lookup = -1;
        static int debug_lookup = -1;
  
        if (debug_lookup < 0)
                printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
                       sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
  
 -      if (use_lookup < 0)
 -              use_lookup = !!getenv("GIT_USE_LOOKUP");
 -      if (use_lookup) {
 -              int pos = sha1_entry_pos(index, stride, 0,
 -                                       lo, hi, p->num_objects, sha1);
 -              if (pos < 0)
 -                      return 0;
 -              return nth_packed_object_offset(p, pos);
 -      }
 -
 -      do {
 +      while (lo < hi) {
                unsigned mi = (lo + hi) / 2;
                int cmp = hashcmp(index + mi * stride, sha1);
  
                        hi = mi;
                else
                        lo = mi+1;
 -      } while (lo < hi);
 +      }
        return 0;
  }
  
@@@ -2906,7 -2746,6 +2907,7 @@@ static int sha1_loose_object_info(cons
        git_zstream stream;
        char hdr[32];
        struct strbuf hdrbuf = STRBUF_INIT;
 +      unsigned long size_scratch;
  
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
         * return value implicitly indicates whether the
         * object even exists.
         */
 -      if (!oi->typep && !oi->typename && !oi->sizep) {
 +      if (!oi->typep && !oi->typename && !oi->sizep && !oi->contentp) {
 +              const char *path;
                struct stat st;
 -              if (stat_sha1_file(sha1, &st) < 0)
 +              if (stat_sha1_file(sha1, &st, &path) < 0)
                        return -1;
                if (oi->disk_sizep)
                        *oi->disk_sizep = st.st_size;
        map = map_sha1_file(sha1, &mapsize);
        if (!map)
                return -1;
 +
 +      if (!oi->sizep)
 +              oi->sizep = &size_scratch;
 +
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
 -      if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
 +      if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) {
                if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
                        status = error("unable to unpack %s header with --allow-unknown-type",
                                       sha1_to_hex(sha1));
                                       sha1_to_hex(sha1));
        } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
 -      git_inflate_end(&stream);
 +
 +      if (status >= 0 && oi->contentp)
 +              *oi->contentp = unpack_sha1_rest(&stream, hdr,
 +                                               *oi->sizep, sha1);
 +      else
 +              git_inflate_end(&stream);
 +
        munmap(map, mapsize);
        if (status && oi->typep)
                *oi->typep = status;
 +      if (oi->sizep == &size_scratch)
 +              oi->sizep = NULL;
        strbuf_release(&hdrbuf);
 -      return 0;
 +      oi->whence = OI_LOOSE;
 +      return (status < 0) ? status : 0;
  }
  
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
  {
 -      struct cached_object *co;
 +      static struct object_info blank_oi = OBJECT_INFO_INIT;
        struct pack_entry e;
        int rtype;
 -      enum object_type real_type;
 -      const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 -
 -      co = find_cached_object(real);
 -      if (co) {
 -              if (oi->typep)
 -                      *(oi->typep) = co->type;
 -              if (oi->sizep)
 -                      *(oi->sizep) = co->size;
 -              if (oi->disk_sizep)
 -                      *(oi->disk_sizep) = 0;
 -              if (oi->delta_base_sha1)
 -                      hashclr(oi->delta_base_sha1);
 -              if (oi->typename)
 -                      strbuf_addstr(oi->typename, typename(co->type));
 -              oi->whence = OI_CACHED;
 -              return 0;
 +      const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ?
 +                                  lookup_replace_object(sha1) :
 +                                  sha1;
 +
 +      if (!oi)
 +              oi = &blank_oi;
 +
 +      if (!(flags & OBJECT_INFO_SKIP_CACHED)) {
 +              struct cached_object *co = find_cached_object(real);
 +              if (co) {
 +                      if (oi->typep)
 +                              *(oi->typep) = co->type;
 +                      if (oi->sizep)
 +                              *(oi->sizep) = co->size;
 +                      if (oi->disk_sizep)
 +                              *(oi->disk_sizep) = 0;
 +                      if (oi->delta_base_sha1)
 +                              hashclr(oi->delta_base_sha1);
 +                      if (oi->typename)
 +                              strbuf_addstr(oi->typename, typename(co->type));
 +                      if (oi->contentp)
 +                              *oi->contentp = xmemdupz(co->buf, co->size);
 +                      oi->whence = OI_CACHED;
 +                      return 0;
 +              }
        }
  
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
 -              if (!sha1_loose_object_info(real, oi, flags)) {
 -                      oi->whence = OI_LOOSE;
 +              if (!sha1_loose_object_info(real, oi, flags))
                        return 0;
 -              }
  
                /* Not a loose object; someone else may have just packed it. */
 -              reprepare_packed_git();
 -              if (!find_pack_entry(real, &e))
 +              if (flags & OBJECT_INFO_QUICK) {
                        return -1;
 +              } else {
 +                      reprepare_packed_git();
 +                      if (!find_pack_entry(real, &e))
 +                              return -1;
 +              }
        }
  
 -      /*
 -       * packed_object_info() does not follow the delta chain to
 -       * find out the real type, unless it is given oi->typep.
 -       */
 -      if (oi->typename && !oi->typep)
 -              oi->typep = &real_type;
 +      if (oi == &blank_oi)
 +              /*
 +               * We know that the caller doesn't actually need the
 +               * information below, so return early.
 +               */
 +              return 0;
  
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
 -              if (oi->typep == &real_type)
 -                      oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
 -      } else if (in_delta_base_cache(e.p, e.offset)) {
 -              oi->whence = OI_DBCACHED;
 -      } else {
 -              oi->whence = OI_PACKED;
 +      } else if (oi->whence == OI_PACKED) {
                oi->u.packed.offset = e.offset;
                oi->u.packed.pack = e.p;
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
 -      if (oi->typename)
 -              strbuf_addstr(oi->typename, typename(*oi->typep));
 -      if (oi->typep == &real_type)
 -              oi->typep = NULL;
  
        return 0;
  }
  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;
 -      if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
 +      if (sha1_object_info_extended(sha1, &oi,
 +                                    OBJECT_INFO_LOOKUP_REPLACE) < 0)
                return -1;
        return type;
  }
  
 -static void *read_packed_sha1(const unsigned char *sha1,
 -                            enum object_type *type, unsigned long *size)
 -{
 -      struct pack_entry e;
 -      void *data;
 -
 -      if (!find_pack_entry(sha1, &e))
 -              return NULL;
 -      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
 -               * the required object anyway from another pack or loose.
 -               * This should happen only in the presence of a corrupted
 -               * pack, and is better than failing outright.
 -               */
 -              error("failed to read object %s at offset %"PRIuMAX" from %s",
 -                    sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name);
 -              mark_bad_packed_object(e.p, sha1);
 -              data = read_object(sha1, type, size);
 -      }
 -      return data;
 -}
 -
  int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
                      unsigned char *sha1)
  {
  static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size)
  {
 -      unsigned long mapsize;
 -      void *map, *buf;
 -      struct cached_object *co;
 +      struct object_info oi = OBJECT_INFO_INIT;
 +      void *content;
 +      oi.typep = type;
 +      oi.sizep = size;
 +      oi.contentp = &content;
  
 -      co = find_cached_object(sha1);
 -      if (co) {
 -              *type = co->type;
 -              *size = co->size;
 -              return xmemdupz(co->buf, co->size);
 -      }
 -
 -      buf = read_packed_sha1(sha1, type, size);
 -      if (buf)
 -              return buf;
 -      map = map_sha1_file(sha1, &mapsize);
 -      if (map) {
 -              buf = unpack_sha1_file(map, mapsize, type, size, sha1);
 -              munmap(map, mapsize);
 -              return buf;
 -      }
 -      reprepare_packed_git();
 -      return read_packed_sha1(sha1, type, size);
 +      if (sha1_object_info_extended(sha1, &oi, 0) < 0)
 +              return NULL;
 +      return content;
  }
  
  /*
  void *read_sha1_file_extended(const unsigned char *sha1,
                              enum object_type *type,
                              unsigned long *size,
 -                            unsigned flag)
 +                            int lookup_replace)
  {
        void *data;
        const struct packed_git *p;
 -      const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
 +      const char *path;
 +      struct stat st;
 +      const unsigned char *repl = lookup_replace ? lookup_replace_object(sha1)
 +                                                 : sha1;
  
        errno = 0;
        data = read_object(repl, type, size);
                die("replacement %s not found for %s",
                    sha1_to_hex(repl), sha1_to_hex(sha1));
  
 -      if (has_loose_object(repl)) {
 -              const char *path = sha1_file_name(sha1);
 -
 +      if (!stat_sha1_file(repl, &st, &path))
                die("loose object %s (stored in %s) is corrupt",
                    sha1_to_hex(repl), path);
 -      }
  
        if ((p = has_packed_and_bad(repl)) != NULL)
                die("packed object %s (stored in %s) is corrupt",
@@@ -3434,7 -3294,7 +3435,7 @@@ int force_object_loose(const unsigned c
  
        if (has_loose_object(sha1))
                return 0;
 -      buf = read_packed_sha1(sha1, &type, &len);
 +      buf = read_object(sha1, &type, &len);
        if (!buf)
                return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
        hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
@@@ -3460,10 -3320,16 +3461,10 @@@ int has_sha1_pack(const unsigned char *
  
  int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
  {
 -      struct pack_entry e;
 -
 -      if (find_pack_entry(sha1, &e))
 -              return 1;
 -      if (has_loose_object(sha1))
 -              return 1;
 -      if (flags & HAS_SHA1_QUICK)
 +      if (!startup_info->have_repository)
                return 0;
 -      reprepare_packed_git();
 -      return find_pack_entry(sha1, &e);
 +      return sha1_object_info_extended(sha1, NULL,
 +                                       flags | OBJECT_INFO_SKIP_CACHED) >= 0;
  }
  
  int has_object_file(const struct object_id *oid)
        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;
@@@ -3519,7 -3380,7 +3520,7 @@@ static int index_mem(unsigned char *sha
         */
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
 -              if (convert_to_git(path, buf, size, &nbuf,
 +              if (convert_to_git(&the_index, path, buf, size, &nbuf,
                                   write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
@@@ -3553,7 -3414,7 +3554,7 @@@ static int index_stream_convert_blob(un
        assert(path);
        assert(would_convert_to_git_filter_fd(path));
  
 -      convert_to_git_filter_fd(path, fd, &sbuf,
 +      convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
                                 write_object ? safe_crlf : SAFE_CRLF_FALSE);
  
        if (write_object)
@@@ -3641,7 -3502,7 +3642,7 @@@ int index_fd(unsigned char *sha1, int f
        else if (!S_ISREG(st->st_mode))
                ret = index_pipe(sha1, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
 -               (path && would_convert_to_git(path)))
 +               (path && would_convert_to_git(&the_index, path)))
                ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
@@@ -3708,32 -3569,22 +3709,32 @@@ void assert_sha1_type(const unsigned ch
                    typename(expect));
  }
  
 -static int for_each_file_in_obj_subdir(int subdir_nr,
 -                                     struct strbuf *path,
 -                                     each_loose_object_fn obj_cb,
 -                                     each_loose_cruft_fn cruft_cb,
 -                                     each_loose_subdir_fn subdir_cb,
 -                                     void *data)
 +int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 +                              struct strbuf *path,
 +                              each_loose_object_fn obj_cb,
 +                              each_loose_cruft_fn cruft_cb,
 +                              each_loose_subdir_fn subdir_cb,
 +                              void *data)
  {
 -      size_t baselen = path->len;
 -      DIR *dir = opendir(path->buf);
 +      size_t origlen, baselen;
 +      DIR *dir;
        struct dirent *de;
        int r = 0;
  
 +      if (subdir_nr > 0xff)
 +              BUG("invalid loose object subdirectory: %x", subdir_nr);
 +
 +      origlen = path->len;
 +      strbuf_complete(path, '/');
 +      strbuf_addf(path, "%02x", subdir_nr);
 +      baselen = path->len;
 +
 +      dir = opendir(path->buf);
        if (!dir) {
 -              if (errno == ENOENT)
 -                      return 0;
 -              return error_errno("unable to open %s", path->buf);
 +              if (errno != ENOENT)
 +                      r = error_errno("unable to open %s", path->buf);
 +              strbuf_setlen(path, origlen);
 +              return r;
        }
  
        while ((de = readdir(dir))) {
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
  
 -              if (strlen(de->d_name) == 38)  {
 -                      char hex[41];
 -                      unsigned char sha1[20];
 +              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
 +                      char hex[GIT_MAX_HEXSZ+1];
 +                      struct object_id oid;
  
 -                      snprintf(hex, sizeof(hex), "%02x%s",
 -                               subdir_nr, de->d_name);
 -                      if (!get_sha1_hex(hex, sha1)) {
 +                      xsnprintf(hex, sizeof(hex), "%02x%s",
 +                                subdir_nr, de->d_name);
 +                      if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
 -                                      r = obj_cb(sha1, path->buf, data);
 +                                      r = obj_cb(&oid, path->buf, data);
                                        if (r)
                                                break;
                                }
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
  
 +      strbuf_setlen(path, origlen);
 +
        return r;
  }
  
@@@ -3782,12 -3631,15 +3783,12 @@@ int for_each_loose_file_in_objdir_buf(s
                            each_loose_subdir_fn subdir_cb,
                            void *data)
  {
 -      size_t baselen = path->len;
        int r = 0;
        int i;
  
        for (i = 0; i < 256; i++) {
 -              strbuf_addf(path, "/%02x", i);
                r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb,
                                                subdir_cb, data);
 -              strbuf_setlen(path, baselen);
                if (r)
                        break;
        }
@@@ -3824,7 -3676,8 +3825,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);
@@@ -3856,13 -3709,13 +3857,13 @@@ static int for_each_object_in_pack(stru
        int r = 0;
  
        for (i = 0; i < p->num_objects; i++) {
 -              const unsigned char *sha1 = nth_packed_object_sha1(p, i);
 +              struct object_id oid;
  
 -              if (!sha1)
 +              if (!nth_packed_object_oid(&oid, p, i))
                        return error("unable to get sha1 of object %u in %s",
                                     i, p->pack_name);
  
 -              r = cb(sha1, p, i, data);
 +              r = cb(&oid, p, i, data);
                if (r)
                        break;
        }
@@@ -3889,119 -3742,3 +3890,119 @@@ int for_each_packed_object(each_packed_
        }
        return r ? r : pack_errors;
  }
 +
 +static int check_stream_sha1(git_zstream *stream,
 +                           const char *hdr,
 +                           unsigned long size,
 +                           const char *path,
 +                           const unsigned char *expected_sha1)
 +{
 +      git_SHA_CTX c;
 +      unsigned char real_sha1[GIT_MAX_RAWSZ];
 +      unsigned char buf[4096];
 +      unsigned long total_read;
 +      int status = Z_OK;
 +
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, stream->total_out);
 +
 +      /*
 +       * We already read some bytes into hdr, but the ones up to the NUL
 +       * do not count against the object's content size.
 +       */
 +      total_read = stream->total_out - strlen(hdr) - 1;
 +
 +      /*
 +       * This size comparison must be "<=" to read the final zlib packets;
 +       * see the comment in unpack_sha1_rest for details.
 +       */
 +      while (total_read <= size &&
 +             (status == Z_OK || status == Z_BUF_ERROR)) {
 +              stream->next_out = buf;
 +              stream->avail_out = sizeof(buf);
 +              if (size - total_read < stream->avail_out)
 +                      stream->avail_out = size - total_read;
 +              status = git_inflate(stream, Z_FINISH);
 +              git_SHA1_Update(&c, buf, stream->next_out - buf);
 +              total_read += stream->next_out - buf;
 +      }
 +      git_inflate_end(stream);
 +
 +      if (status != Z_STREAM_END) {
 +              error("corrupt loose object '%s'", sha1_to_hex(expected_sha1));
 +              return -1;
 +      }
 +      if (stream->avail_in) {
 +              error("garbage at end of loose object '%s'",
 +                    sha1_to_hex(expected_sha1));
 +              return -1;
 +      }
 +
 +      git_SHA1_Final(real_sha1, &c);
 +      if (hashcmp(expected_sha1, real_sha1)) {
 +              error("sha1 mismatch for %s (expected %s)", path,
 +                    sha1_to_hex(expected_sha1));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +int read_loose_object(const char *path,
 +                    const unsigned char *expected_sha1,
 +                    enum object_type *type,
 +                    unsigned long *size,
 +                    void **contents)
 +{
 +      int ret = -1;
 +      void *map = NULL;
 +      unsigned long mapsize;
 +      git_zstream stream;
 +      char hdr[32];
 +
 +      *contents = NULL;
 +
 +      map = map_sha1_file_1(path, NULL, &mapsize);
 +      if (!map) {
 +              error_errno("unable to mmap %s", path);
 +              goto out;
 +      }
 +
 +      if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) {
 +              error("unable to unpack header of %s", path);
 +              goto out;
 +      }
 +
 +      *type = parse_sha1_header(hdr, size);
 +      if (*type < 0) {
 +              error("unable to parse header of %s", path);
 +              git_inflate_end(&stream);
 +              goto out;
 +      }
 +
 +      if (*type == OBJ_BLOB) {
 +              if (check_stream_sha1(&stream, hdr, *size, path, expected_sha1) < 0)
 +                      goto out;
 +      } else {
 +              *contents = unpack_sha1_rest(&stream, hdr, *size, expected_sha1);
 +              if (!*contents) {
 +                      error("unable to unpack contents of %s", path);
 +                      git_inflate_end(&stream);
 +                      goto out;
 +              }
 +              if (check_sha1_signature(expected_sha1, *contents,
 +                                       *size, typename(*type))) {
 +                      error("sha1 mismatch for %s (expected %s)", path,
 +                            sha1_to_hex(expected_sha1));
 +                      free(*contents);
 +                      goto out;
 +              }
 +      }
 +
 +      ret = 0; /* everything checks out */
 +
 +out:
 +      if (map)
 +              munmap(map, mapsize);
 +      return ret;
 +}