Merge branch 'jc/maint-rerere-in-workdir'
authorJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:16 +0000 (20:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 27 Mar 2011 03:13:16 +0000 (20:13 -0700)
* jc/maint-rerere-in-workdir:
  rerere: make sure it works even in a workdir attached to a young repository

1  2 
cache.h
rerere.c
sha1_file.c

diff --combined cache.h
+++ b/cache.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "strbuf.h"
  #include "hash.h"
  #include "advice.h"
 +#include "gettext.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -171,26 -170,27 +171,26 @@@ struct cache_entry 
   *
   * In-memory only flags
   */
 -#define CE_UPDATE    (0x10000)
 -#define CE_REMOVE    (0x20000)
 -#define CE_UPTODATE  (0x40000)
 -#define CE_ADDED     (0x80000)
 +#define CE_UPDATE            (1 << 16)
 +#define CE_REMOVE            (1 << 17)
 +#define CE_UPTODATE          (1 << 18)
 +#define CE_ADDED             (1 << 19)
  
 -#define CE_HASHED    (0x100000)
 -#define CE_UNHASHED  (0x200000)
 -#define CE_CONFLICTED (0x800000)
 +#define CE_HASHED            (1 << 20)
 +#define CE_UNHASHED          (1 << 21)
 +#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 +#define CE_CONFLICTED        (1 << 23)
  
 -/* Only remove in work directory, not index */
 -#define CE_WT_REMOVE (0x400000)
 -
 -#define CE_UNPACKED  (0x1000000)
 +#define CE_UNPACKED          (1 << 24)
 +#define CE_NEW_SKIP_WORKTREE (1 << 25)
  
  /*
   * Extended on-disk flags
   */
 -#define CE_INTENT_TO_ADD 0x20000000
 -#define CE_SKIP_WORKTREE 0x40000000
 +#define CE_INTENT_TO_ADD     (1 << 29)
 +#define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2 0x80000000
 +#define CE_EXTENDED2         (1 << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
@@@ -278,16 -278,9 +278,16 @@@ static inline int ce_to_dtype(const str
        else
                return DT_UNKNOWN;
  }
 -#define canon_mode(mode) \
 -      (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 -      S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 +static inline unsigned int canon_mode(unsigned int mode)
 +{
 +      if (S_ISREG(mode))
 +              return S_IFREG | ce_permissions(mode);
 +      if (S_ISLNK(mode))
 +              return S_IFLNK;
 +      if (S_ISDIR(mode))
 +              return S_IFDIR;
 +      return S_IFGITLINK;
 +}
  
  #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) flexible_size(cache_entry,len)
@@@ -368,7 -361,7 +368,7 @@@ enum object_type 
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
        OBJ_ANY,
 -      OBJ_MAX,
 +      OBJ_MAX
  };
  
  static inline enum object_type object_type(unsigned int mode)
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
  #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 +#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
  #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_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
 +#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
  
  /*
   * Repository-local GIT_* environment variables
   * environment creation or simple walk of the list.
   * The number of non-NULL entries is available as a macro.
   */
 -#define LOCAL_REPO_ENV_SIZE 8
 +#define LOCAL_REPO_ENV_SIZE 9
  extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
  
  extern int is_bare_repository_cfg;
@@@ -429,7 -418,7 +429,7 @@@ extern const char **get_pathspec(const 
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
 -extern const char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
@@@ -446,7 -435,7 +446,7 @@@ extern int init_db(const char *template
   * at least 'nr' entries; the number of entries currently allocated
   * is 'alloc', using the standard growing factor alloc_nr() macro.
   *
 - * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
 + * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
   */
  #define ALLOC_GROW(x, nr, alloc) \
        do { \
                                alloc = alloc_nr(alloc); \
                        x = xrealloc((x), alloc * sizeof(*(x))); \
                } \
 -      } while(0)
 +      } while (0)
  
  /* Initialize and use the cache information */
  extern int read_index(struct index_state *);
@@@ -501,23 -490,8 +501,23 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 +struct pathspec {
 +      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 +      int nr;
 +      unsigned int has_wildcard:1;
 +      unsigned int recursive:1;
 +      int max_depth;
 +      struct pathspec_item {
 +              const char *match;
 +              int len;
 +              unsigned int has_wildcard:1;
 +      } *items;
 +};
 +
 +extern int init_pathspec(struct pathspec *, const char **);
 +extern void free_pathspec(struct pathspec *);
 +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -556,7 -530,6 +556,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -571,6 -544,7 +571,6 @@@ extern int core_compression_seen
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
 -extern int auto_crlf;
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
@@@ -579,53 -553,32 +579,53 @@@ extern int core_apply_sparse_checkout
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
 -      SAFE_CRLF_WARN = 2,
 +      SAFE_CRLF_WARN = 2
  };
  
  extern enum safe_crlf safe_crlf;
  
 +enum auto_crlf {
 +      AUTO_CRLF_FALSE = 0,
 +      AUTO_CRLF_TRUE = 1,
 +      AUTO_CRLF_INPUT = -1
 +};
 +
 +extern enum auto_crlf auto_crlf;
 +
 +enum eol {
 +      EOL_UNSET,
 +      EOL_CRLF,
 +      EOL_LF,
 +#ifdef NATIVE_CRLF
 +      EOL_NATIVE = EOL_CRLF
 +#else
 +      EOL_NATIVE = EOL_LF
 +#endif
 +};
 +
 +extern enum eol eol;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
 -      BRANCH_TRACK_OVERRIDE,
 +      BRANCH_TRACK_OVERRIDE
  };
  
  enum rebase_setup_type {
        AUTOREBASE_NEVER = 0,
        AUTOREBASE_LOCAL,
        AUTOREBASE_REMOTE,
 -      AUTOREBASE_ALWAYS,
 +      AUTOREBASE_ALWAYS
  };
  
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 -      PUSH_DEFAULT_TRACKING,
 -      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UPSTREAM,
 +      PUSH_DEFAULT_CURRENT
  };
  
  extern enum branch_track git_branch_track;
@@@ -634,7 -587,7 +634,7 @@@ extern enum push_default_type push_defa
  
  enum object_creation_mode {
        OBJECT_CREATION_USES_HARDLINKS = 0,
 -      OBJECT_CREATION_USES_RENAMES = 1,
 +      OBJECT_CREATION_USES_RENAMES = 1
  };
  
  extern enum object_creation_mode object_creation_mode;
@@@ -665,9 -618,6 +665,9 @@@ extern char *git_pathdup(const char *fm
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern char *git_path_submodule(const char *path, const char *fmt, ...)
 +      __attribute__((format (printf, 2, 3)));
 +
  extern char *sha1_file_name(const unsigned char *sha1);
  extern char *sha1_pack_name(const unsigned char *sha1);
  extern char *sha1_pack_index_name(const unsigned char *sha1);
@@@ -692,11 -642,9 +692,11 @@@ static inline void hashclr(unsigned cha
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 -#define EMPTY_TREE_SHA1_BIN \
 +#define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 +#define EMPTY_TREE_SHA1_BIN \
 +       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
@@@ -719,13 -667,14 +719,14 @@@ enum sharedrepo 
        OLD_PERM_GROUP      = 1,
        OLD_PERM_EVERYBODY  = 2,
        PERM_GROUP          = 0660,
 -      PERM_EVERYBODY      = 0664,
 +      PERM_EVERYBODY      = 0664
  };
  int git_config_perm(const char *var, const char *value);
  int set_shared_perm(const char *path, int mode);
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
+ int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
        return path[0] == '/' || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 -const char *make_absolute_path(const char *path);
 -const char *make_nonrelative_path(const char *path);
 -const char *make_relative_path(const char *abs, const char *base);
 +const char *real_path(const char *path);
 +const char *absolute_path(const char *path);
 +const char *relative_path(const char *abs, const char *base);
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
 +int offset_1st_component(const char *path);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -750,7 -698,7 +751,7 @@@ static inline void *read_sha1_file(cons
        return read_sha1_file_repl(sha1, type, size, NULL);
  }
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 -extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 +extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  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);
  
@@@ -767,8 -715,6 +768,8 @@@ extern int has_loose_object_nonlocal(co
  
  extern int has_pack_index(const unsigned char *sha1);
  
 +extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 +
  extern const signed char hexval_table[256];
  static inline unsigned int hexval(unsigned char c)
  {
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
 +
 +struct object_context {
 +      unsigned char tree[20];
 +      char path[PATH_MAX];
 +      unsigned mode;
 +};
  
  extern int get_sha1(const char *str, unsigned char *sha1);
  extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
@@@ -791,11 -731,6 +792,11 @@@ static inline int get_sha1_with_mode(co
  {
        return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
  }
 +extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
 +static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
 +{
 +      return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
@@@ -840,7 -775,6 +841,7 @@@ const char *show_date_relative(unsigne
                               char *timebuf,
                               size_t timebuf_size);
  int parse_date(const char *date, char *buf, int bufsize);
 +int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
  #define approxidate(s) approxidate_careful((s), NULL)
  unsigned long approxidate_careful(const char *, int *);
@@@ -878,7 -812,7 +879,7 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 -extern int has_symlink_or_noent_leading_path(const char *name, int len);
 +extern int check_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
@@@ -915,8 -849,7 +916,8 @@@ extern struct packed_git 
        time_t mtime;
        int pack_fd;
        unsigned pack_local:1,
 -               pack_keep:1;
 +               pack_keep:1,
 +               do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -944,7 -877,7 +945,7 @@@ struct ref 
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
 -              REF_STATUS_EXPECTING_REPORT,
 +              REF_STATUS_EXPECTING_REPORT
        } status;
        char *remote_status;
        struct ref *peer_ref; /* when renaming */
  extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
  
  #define CONNECT_VERBOSE       (1u << 0)
 +extern char *git_getpass(const char *prompt);
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
@@@ -969,7 -901,7 +970,7 @@@ struct extra_have_objects 
  extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
 -extern struct packed_git *parse_pack_index(unsigned char *sha1);
 +extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  extern void prepare_packed_git(void);
  extern void reprepare_packed_git(void);
@@@ -980,7 -912,6 +981,7 @@@ extern struct packed_git *find_sha1_pac
  
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
 +extern void close_pack_index(struct packed_git *);
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
@@@ -1001,32 -932,22 +1002,32 @@@ extern int update_server_info(int)
  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 void git_config_push_parameter(const char *text);
 +extern int git_config_parse_parameter(const char *text);
 +extern int git_config_parse_environment(void);
 +extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(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(const char *, const char *);
 +extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
 +extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
 -extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1066,7 -987,6 +1067,7 @@@ extern int pager_in_use(void)
  extern int pager_use_color;
  
  extern const char *editor_program;
 +extern const char *askpass_program;
  extern const char *excludes_file;
  
  /* base85 */
@@@ -1084,21 -1004,14 +1085,21 @@@ extern void alloc_report(void)
  /* trace.c */
  __attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +extern void trace_vprintf(const char *key, const char *format, va_list ap);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
 +extern void trace_repo_setup(const char *prefix);
 +extern int trace_want(const char *key);
 +extern void trace_strbuf(const char *key, const struct strbuf *buf);
 +
 +void packet_trace_identity(const char *prog);
  
  /* convert.c */
  /* returns 1 if *dst was used */
  extern int convert_to_git(const char *path, const char *src, size_t len,
                            struct strbuf *dst, enum safe_crlf checksafe);
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 +extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
  /*
@@@ -1117,26 -1030,22 +1118,26 @@@ void shift_tree_by(const unsigned char 
  /*
   * whitespace rules.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
  extern char *whitespace_error_string(unsigned ws);
 -extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
 +extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1144,17 -1053,5 +1145,17 @@@ void overlay_tree_on_cache(const char *
  
  char *alias_lookup(const char *alias);
  int split_cmdline(char *cmdline, const char ***argv);
 +/* Takes a negative value returned by split_cmdline */
 +const char *split_cmdline_strerror(int cmdline_errno);
 +
 +/* git.c */
 +struct startup_info {
 +      int have_repository;
 +      const char *prefix;
 +};
 +extern struct startup_info *startup_info;
 +
 +/* builtin/merge.c */
 +int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
  #endif /* CACHE_H */
diff --combined rerere.c
+++ b/rerere.c
@@@ -7,11 -7,6 +7,11 @@@
  #include "ll-merge.h"
  #include "attr.h"
  
 +#define RESOLVED 0
 +#define PUNTED 1
 +#define THREE_STAGED 2
 +void *RERERE_RESOLVED = &RERERE_RESOLVED;
 +
  /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
  static int rerere_enabled = -1;
  
@@@ -51,7 -46,7 +51,7 @@@ static void read_rr(struct string_list 
                        ; /* do nothing */
                if (i == sizeof(buf))
                        die("filename too long");
 -              string_list_insert(buf, rr)->util = name;
 +              string_list_insert(rr, buf)->util = name;
        }
        fclose(in);
  }
@@@ -158,7 -153,7 +158,7 @@@ static int handle_path(unsigned char *s
        git_SHA_CTX ctx;
        int hunk_no = 0;
        enum {
 -              RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
 +              RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
        } hunk = RR_CONTEXT;
        struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
@@@ -324,13 -319,9 +324,13 @@@ static int handle_cache(const char *pat
                if (!mmfile[i].ptr && !mmfile[i].size)
                        mmfile[i].ptr = xstrdup("");
        }
 -      ll_merge(&result, path, &mmfile[0],
 +      /*
 +       * NEEDSWORK: handle conflicts from merges with
 +       * merge.renormalize set, too
 +       */
 +      ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
 -               &mmfile[2], "theirs", 0);
 +               &mmfile[2], "theirs", NULL);
        for (i = 0; i < 3; i++)
                free(mmfile[i].ptr);
  
        return hunk_no;
  }
  
 -static int find_conflict(struct string_list *conflict)
 +static int check_one_conflict(int i, int *type)
  {
 -      int i;
 -      if (read_cache() < 0)
 -              return error("Could not read index");
 -      for (i = 0; i+1 < active_nr; i++) {
 +      struct cache_entry *e = active_cache[i];
 +
 +      if (!ce_stage(e)) {
 +              *type = RESOLVED;
 +              return i + 1;
 +      }
 +
 +      *type = PUNTED;
 +      if (ce_stage(e) == 1) {
 +              if (active_nr <= ++i)
 +                      return i + 1;
 +      }
 +
 +      /* Only handle regular files with both stages #2 and #3 */
 +      if (i + 1 < active_nr) {
                struct cache_entry *e2 = active_cache[i];
 -              struct cache_entry *e3 = active_cache[i+1];
 +              struct cache_entry *e3 = active_cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
 -                  ce_same_name(e2, e3) &&
 +                  ce_same_name(e, e3) &&
                    S_ISREG(e2->ce_mode) &&
 -                  S_ISREG(e3->ce_mode)) {
 -                      string_list_insert((const char *)e2->name, conflict);
 -                      i++; /* skip over both #2 and #3 */
 +                  S_ISREG(e3->ce_mode))
 +                      *type = THREE_STAGED;
 +      }
 +
 +      /* Skip the entries with the same name */
 +      while (i < active_nr && ce_same_name(e, active_cache[i]))
 +              i++;
 +      return i;
 +}
 +
 +static int find_conflict(struct string_list *conflict)
 +{
 +      int i;
 +      if (read_cache() < 0)
 +              return error("Could not read index");
 +
 +      for (i = 0; i < active_nr;) {
 +              int conflict_type;
 +              struct cache_entry *e = active_cache[i];
 +              i = check_one_conflict(i, &conflict_type);
 +              if (conflict_type == THREE_STAGED)
 +                      string_list_insert(conflict, (const char *)e->name);
 +      }
 +      return 0;
 +}
 +
 +int rerere_remaining(struct string_list *merge_rr)
 +{
 +      int i;
 +      if (read_cache() < 0)
 +              return error("Could not read index");
 +
 +      for (i = 0; i < active_nr;) {
 +              int conflict_type;
 +              struct cache_entry *e = active_cache[i];
 +              i = check_one_conflict(i, &conflict_type);
 +              if (conflict_type == PUNTED)
 +                      string_list_insert(merge_rr, (const char *)e->name);
 +              else if (conflict_type == RESOLVED) {
 +                      struct string_list_item *it;
 +                      it = string_list_lookup(merge_rr, (const char *)e->name);
 +                      if (it != NULL) {
 +                              free(it->util);
 +                              it->util = RERERE_RESOLVED;
 +                      }
                }
        }
        return 0;
@@@ -438,15 -376,9 +438,15 @@@ static int merge(const char *name, cons
                ret = 1;
                goto out;
        }
 -      ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0);
 +      ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", 0);
        if (!ret) {
 -              FILE *f = fopen(path, "w");
 +              FILE *f;
 +
 +              if (utime(rerere_path(name, "postimage"), NULL) < 0)
 +                      warning("failed utime() on %s: %s",
 +                                      rerere_path(name, "postimage"),
 +                                      strerror(errno));
 +              f = fopen(path, "w");
                if (!f)
                        return error("Could not open %s: %s", path,
                                     strerror(errno));
@@@ -494,8 -426,8 +494,8 @@@ static int update_paths(struct string_l
  
  static int do_plain_rerere(struct string_list *rr, int fd)
  {
 -      struct string_list conflict = { NULL, 0, 0, 1 };
 -      struct string_list update = { NULL, 0, 0, 1 };
 +      struct string_list conflict = STRING_LIST_INIT_DUP;
 +      struct string_list update = STRING_LIST_INIT_DUP;
        int i;
  
        find_conflict(&conflict);
                        if (ret < 1)
                                continue;
                        hex = xstrdup(sha1_to_hex(sha1));
 -                      string_list_insert(path, rr)->util = hex;
 +                      string_list_insert(rr, path)->util = hex;
                        if (mkdir(git_path("rr-cache/%s", hex), 0755))
                                continue;
                        handle_file(path, NULL, rerere_path(hex, "preimage"));
                if (has_rerere_resolution(name)) {
                        if (!merge(name, path)) {
                                if (rerere_autoupdate)
 -                                      string_list_insert(path, &update);
 +                                      string_list_insert(&update, path);
                                fprintf(stderr,
                                        "%s '%s' using previous resolution.\n",
                                        rerere_autoupdate
@@@ -590,8 -522,7 +590,7 @@@ static int is_rerere_enabled(void
        if (rerere_enabled < 0)
                return rr_cache_exists;
  
-       if (!rr_cache_exists &&
-           (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
+       if (!rr_cache_exists && mkdir_in_gitdir(rr_cache))
                die("Could not create directory %s", rr_cache);
        return 1;
  }
@@@ -615,7 -546,7 +614,7 @@@ int setup_rerere(struct string_list *me
  
  int rerere(int flags)
  {
 -      struct string_list merge_rr = { NULL, 0, 0, 1 };
 +      struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd;
  
        fd = setup_rerere(&merge_rr, flags);
@@@ -645,7 -576,7 +644,7 @@@ static int rerere_forget_one_path(cons
        fprintf(stderr, "Updated preimage for '%s'\n", path);
  
  
 -      string_list_insert(path, rr)->util = hex;
 +      string_list_insert(rr, path)->util = hex;
        fprintf(stderr, "Forgot resolution for %s\n", path);
        return 0;
  }
  int rerere_forget(const char **pathspec)
  {
        int i, fd;
 -      struct string_list conflict = { NULL, 0, 0, 1 };
 -      struct string_list merge_rr = { NULL, 0, 0, 1 };
 +      struct string_list conflict = STRING_LIST_INIT_DUP;
 +      struct string_list merge_rr = STRING_LIST_INIT_DUP;
  
        if (read_cache() < 0)
                return error("Could not read index");
diff --combined sha1_file.c
@@@ -13,7 -13,6 +13,7 @@@
  #include "commit.h"
  #include "tag.h"
  #include "tree.h"
 +#include "tree-walk.h"
  #include "refs.h"
  #include "pack-revindex.h"
  #include "sha1-lookup.h"
  #endif
  #endif
  
 -#ifdef NO_C99_FORMAT
 -#define SZ_FMT "lu"
 -static unsigned long sz_fmt(size_t s) { return (unsigned long)s; }
 -#else
 -#define SZ_FMT "zu"
 -static size_t sz_fmt(size_t s) { return s; }
 -#endif
 +#define SZ_FMT PRIuMAX
 +static inline uintmax_t sz_fmt(size_t s) { return s; }
  
  const unsigned char null_sha1[20];
  
 -static inline int offset_1st_component(const char *path)
 +static int git_open_noatime(const char *name, struct packed_git *p);
 +
 +/*
 + * This is meant to hold a *small* number of objects that you would
 + * want read_sha1_file() to be able to return, but yet you do not want
 + * to write them into the object store (e.g. a browse-only
 + * application).
 + */
 +static struct cached_object {
 +      unsigned char sha1[20];
 +      enum object_type type;
 +      void *buf;
 +      unsigned long size;
 +} *cached_objects;
 +static int cached_object_nr, cached_object_alloc;
 +
 +static struct cached_object empty_tree = {
 +      EMPTY_TREE_SHA1_BIN_LITERAL,
 +      OBJ_TREE,
 +      "",
 +      0
 +};
 +
 +static struct cached_object *find_cached_object(const unsigned char *sha1)
  {
 -      if (has_dos_drive_prefix(path))
 -              return 2 + (path[2] == '/');
 -      return *path == '/';
 +      int i;
 +      struct cached_object *co = cached_objects;
 +
 +      for (i = 0; i < cached_object_nr; i++, co++) {
 +              if (!hashcmp(co->sha1, sha1))
 +                      return co;
 +      }
 +      if (!hashcmp(sha1, empty_tree.sha1))
 +              return &empty_tree;
 +      return NULL;
  }
  
+ int mkdir_in_gitdir(const char *path)
+ {
+       if (mkdir(path, 0777)) {
+               int saved_errno = errno;
+               struct stat st;
+               struct strbuf sb = STRBUF_INIT;
+               if (errno != EEXIST)
+                       return -1;
+               /*
+                * Are we looking at a path in a symlinked worktree
+                * whose original repository does not yet have it?
+                * e.g. .git/rr-cache pointing at its original
+                * repository in which the user hasn't performed any
+                * conflict resolution yet?
+                */
+               if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||
+                   strbuf_readlink(&sb, path, st.st_size) ||
+                   !is_absolute_path(sb.buf) ||
+                   mkdir(sb.buf, 0777)) {
+                       strbuf_release(&sb);
+                       errno = saved_errno;
+                       return -1;
+               }
+               strbuf_release(&sb);
+       }
+       return adjust_shared_perm(path);
+ }
  int safe_create_leading_directories(char *path)
  {
        char *pos = path + offset_1st_component(path);
@@@ -135,22 -138,20 +164,22 @@@ static void fill_sha1_path(char *pathbu
   */
  char *sha1_file_name(const unsigned char *sha1)
  {
 -      static char *name, *base;
 +      static char buf[PATH_MAX];
 +      const char *objdir;
 +      int len;
  
 -      if (!base) {
 -              const char *sha1_file_directory = get_object_directory();
 -              int len = strlen(sha1_file_directory);
 -              base = xmalloc(len + 60);
 -              memcpy(base, sha1_file_directory, len);
 -              memset(base+len, 0, 60);
 -              base[len] = '/';
 -              base[len+3] = '/';
 -              name = base + len + 1;
 -      }
 -      fill_sha1_path(name, sha1);
 -      return base;
 +      objdir = get_object_directory();
 +      len = strlen(objdir);
 +
 +      /* '/' + 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;
  }
  
  static char *sha1_get_pack_name(const unsigned char *sha1,
@@@ -331,7 -332,7 +360,7 @@@ static void read_info_alternates(const 
        int fd;
  
        sprintf(path, "%s/%s", relative_base, alt_file_name);
 -      fd = open(path, O_RDONLY);
 +      fd = git_open_noatime(path, NULL);
        if (fd < 0)
                return;
        if (fstat(fd, &st) || (st.st_size == 0)) {
@@@ -413,8 -414,6 +442,8 @@@ static unsigned int pack_used_ctr
  static unsigned int pack_mmap_calls;
  static unsigned int peak_pack_open_windows;
  static unsigned int pack_open_windows;
 +static unsigned int pack_open_fds;
 +static unsigned int pack_max_fds;
  static size_t peak_pack_mapped;
  static size_t pack_mapped;
  struct packed_git *packed_git;
@@@ -446,7 -445,7 +475,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 = open(path, O_RDONLY);
 +      int fd = git_open_noatime(path, p);
        struct stat st;
  
        if (fd < 0)
@@@ -592,10 -591,8 +621,10 @@@ static int unuse_one_window(struct pack
                        lru_l->next = lru_w->next;
                else {
                        lru_p->windows = lru_w->next;
 -                      if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
 +                      if (!lru_p->windows && lru_p->pack_fd != -1
 +                              && lru_p->pack_fd != keep_fd) {
                                close(lru_p->pack_fd);
 +                              pack_open_fds--;
                                lru_p->pack_fd = -1;
                        }
                }
@@@ -613,21 -610,6 +642,21 @@@ void release_pack_memory(size_t need, i
                ; /* nothing */
  }
  
 +void *xmmap(void *start, size_t length,
 +      int prot, int flags, int fd, off_t offset)
 +{
 +      void *ret = mmap(start, length, prot, flags, fd, offset);
 +      if (ret == MAP_FAILED) {
 +              if (!length)
 +                      return NULL;
 +              release_pack_memory(length, fd);
 +              ret = mmap(start, length, prot, flags, fd, offset);
 +              if (ret == MAP_FAILED)
 +                      die_errno("Out of memory? mmap failed");
 +      }
 +      return ret;
 +}
 +
  void close_pack_windows(struct packed_git *p)
  {
        while (p->windows) {
@@@ -653,14 -635,6 +682,14 @@@ void unuse_pack(struct pack_window **w_
        }
  }
  
 +void close_pack_index(struct packed_git *p)
 +{
 +      if (p->index_data) {
 +              munmap((void *)p->index_data, p->index_size);
 +              p->index_data = NULL;
 +      }
 +}
 +
  /*
   * This is used by git-repack in case a newly created pack happens to
   * contain the same set of objects as an existing one.  In that case
@@@ -680,11 -654,10 +709,11 @@@ void free_pack_by_name(const char *pack
                if (strcmp(pack_name, p->pack_name) == 0) {
                        clear_delta_base_cache();
                        close_pack_windows(p);
 -                      if (p->pack_fd != -1)
 +                      if (p->pack_fd != -1) {
                                close(p->pack_fd);
 -                      if (p->index_data)
 -                              munmap((void *)p->index_data, p->index_size);
 +                              pack_open_fds--;
 +                      }
 +                      close_pack_index(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        free(p);
@@@ -709,29 -682,11 +738,29 @@@ static int open_packed_git_1(struct pac
        if (!p->index_data && open_pack_index(p))
                return error("packfile %s index unavailable", p->pack_name);
  
 -      p->pack_fd = open(p->pack_name, O_RDONLY);
 -      while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1))
 -              p->pack_fd = open(p->pack_name, O_RDONLY);
 +      if (!pack_max_fds) {
 +              struct rlimit lim;
 +              unsigned int max_fds;
 +
 +              if (getrlimit(RLIMIT_NOFILE, &lim))
 +                      die_errno("cannot get RLIMIT_NOFILE");
 +
 +              max_fds = lim.rlim_cur;
 +
 +              /* Save 3 for stdin/stdout/stderr, 22 for work */
 +              if (25 < max_fds)
 +                      pack_max_fds = max_fds - 25;
 +              else
 +                      pack_max_fds = 1;
 +      }
 +
 +      while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
 +              ; /* nothing */
 +
 +      p->pack_fd = git_open_noatime(p->pack_name, p);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
 +      pack_open_fds++;
  
        /* If we created the struct before we had the pack we lack size. */
        if (!p->pack_size) {
@@@ -783,7 -738,6 +812,7 @@@ static int open_packed_git(struct packe
                return 0;
        if (p->pack_fd != -1) {
                close(p->pack_fd);
 +              pack_open_fds--;
                p->pack_fd = -1;
        }
        return -1;
@@@ -809,13 -763,14 +838,13 @@@ unsigned char *use_pack(struct packed_g
  {
        struct pack_window *win = *w_cursor;
  
 -      if (p->pack_fd == -1 && open_packed_git(p))
 -              die("packfile %s cannot be accessed", p->pack_name);
 -
        /* Since packfiles end in a hash of their content and it's
         * pointless to ask for an offset into the middle of that
         * hash, and the in_window function above wouldn't match
         * don't allow an offset too close to the end of the file.
         */
 +      if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p))
 +              die("packfile %s cannot be accessed", p->pack_name);
        if (offset > (p->pack_size - 20))
                die("offset beyond end of packfile (truncated pack?)");
  
                if (!win) {
                        size_t window_align = packed_git_window_size / 2;
                        off_t len;
 +
 +                      if (p->pack_fd == -1 && open_packed_git(p))
 +                              die("packfile %s cannot be accessed", p->pack_name);
 +
                        win = xcalloc(1, sizeof(*win));
                        win->offset = (offset / window_align) * window_align;
                        len = p->pack_size - win->offset;
                                die("packfile %s cannot be mapped: %s",
                                        p->pack_name,
                                        strerror(errno));
 +                      if (!win->offset && win->len == p->pack_size
 +                              && !p->do_not_close) {
 +                              close(p->pack_fd);
 +                              pack_open_fds--;
 +                              p->pack_fd = -1;
 +                      }
                        pack_mmap_calls++;
                        pack_open_windows++;
                        if (pack_mapped > peak_pack_mapped)
@@@ -885,22 -830,11 +914,22 @@@ static struct packed_git *alloc_packed_
        return p;
  }
  
 +static void try_to_free_pack_memory(size_t size)
 +{
 +      release_pack_memory(size, -1);
 +}
 +
  struct packed_git *add_packed_git(const char *path, int path_len, int local)
  {
 +      static int have_set_try_to_free_routine;
        struct stat st;
        struct packed_git *p = alloc_packed_git(path_len + 2);
  
 +      if (!have_set_try_to_free_routine) {
 +              have_set_try_to_free_routine = 1;
 +              set_try_to_free_routine(try_to_free_pack_memory);
 +      }
 +
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
        return p;
  }
  
 -struct packed_git *parse_pack_index(unsigned char *sha1)
 +struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
  {
 -      const char *idx_path = sha1_pack_index_name(sha1);
        const char *path = sha1_pack_name(sha1);
        struct packed_git *p = alloc_packed_git(strlen(path) + 1);
  
  
  void install_packed_git(struct packed_git *pack)
  {
 +      if (pack->pack_fd != -1)
 +              pack_open_fds++;
 +
        pack->next = packed_git;
        packed_git = pack;
  }
@@@ -970,6 -902,8 +999,6 @@@ static void prepare_packed_git_one(cha
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
        dir = opendir(path);
 -      while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1))
 -              dir = opendir(path);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
@@@ -1097,7 -1031,7 +1126,7 @@@ static void mark_bad_packed_object(stru
        p->num_bad_objects++;
  }
  
 -static int has_packed_and_bad(const unsigned char *sha1)
 +static const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
  {
        struct packed_git *p;
        unsigned i;
        for (p = packed_git; p; p = p->next)
                for (i = 0; i < p->num_bad_objects; i++)
                        if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
 -                              return 1;
 -      return 0;
 +                              return p;
 +      return NULL;
  }
  
  int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
 -static int git_open_noatime(const char *name)
 +static int git_open_noatime(const char *name, struct packed_git *p)
  {
        static int sha1_file_open_flag = O_NOATIME;
 -      int fd = open(name, O_RDONLY | sha1_file_open_flag);
  
 -      /* Might the failure be due to O_NOATIME? */
 -      if (fd < 0 && errno != ENOENT && sha1_file_open_flag) {
 -              fd = open(name, O_RDONLY);
 +      for (;;) {
 +              int fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
 +                      return fd;
 +
 +              /* Might the failure be due to O_NOATIME? */
 +              if (errno != ENOENT && sha1_file_open_flag) {
                        sha1_file_open_flag = 0;
 +                      continue;
 +              }
 +
 +              return -1;
        }
 -      return fd;
  }
  
  static int open_sha1_file(const unsigned char *sha1)
        char *name = sha1_file_name(sha1);
        struct alternate_object_database *alt;
  
 -      fd = git_open_noatime(name);
 +      fd = git_open_noatime(name, NULL);
        if (fd >= 0)
                return fd;
  
        for (alt = alt_odb_list; alt; alt = alt->next) {
                name = alt->name;
                fill_sha1_path(name, sha1);
 -              fd = git_open_noatime(alt->base);
 +              fd = git_open_noatime(alt->base, NULL);
                if (fd >= 0)
                        return fd;
        }
@@@ -1956,27 -1885,6 +1985,27 @@@ off_t find_pack_entry_one(const unsigne
        return 0;
  }
  
 +static int is_pack_valid(struct packed_git *p)
 +{
 +      /* An already open pack is known to be valid. */
 +      if (p->pack_fd != -1)
 +              return 1;
 +
 +      /* If the pack has one window completely covering the
 +       * file size, the pack is known to be valid even if
 +       * the descriptor is not currently open.
 +       */
 +      if (p->windows) {
 +              struct pack_window *w = p->windows;
 +
 +              if (!w->offset && w->len == p->pack_size)
 +                      return 1;
 +      }
 +
 +      /* Force the pack to open to prove its valid. */
 +      return !open_packed_git(p);
 +}
 +
  static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
  {
        static struct packed_git *last_found = (void *)1;
                         * it may have been deleted since the index
                         * was loaded!
                         */
 -                      if (p->pack_fd == -1 && open_packed_git(p)) {
 +                      if (!is_pack_valid(p)) {
                                error("packfile %s cannot be accessed", p->pack_name);
                                goto next;
                        }
@@@ -2066,17 -1974,9 +2095,17 @@@ static int sha1_loose_object_info(cons
  
  int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
  {
 +      struct cached_object *co;
        struct pack_entry e;
        int status;
  
 +      co = find_cached_object(sha1);
 +      if (co) {
 +              if (sizep)
 +                      *sizep = co->size;
 +              return co->type;
 +      }
 +
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
                status = sha1_loose_object_info(sha1, sizep);
@@@ -2122,6 -2022,41 +2151,6 @@@ static void *read_packed_sha1(const uns
        return data;
  }
  
 -/*
 - * This is meant to hold a *small* number of objects that you would
 - * want read_sha1_file() to be able to return, but yet you do not want
 - * to write them into the object store (e.g. a browse-only
 - * application).
 - */
 -static struct cached_object {
 -      unsigned char sha1[20];
 -      enum object_type type;
 -      void *buf;
 -      unsigned long size;
 -} *cached_objects;
 -static int cached_object_nr, cached_object_alloc;
 -
 -static struct cached_object empty_tree = {
 -      EMPTY_TREE_SHA1_BIN,
 -      OBJ_TREE,
 -      "",
 -      0
 -};
 -
 -static struct cached_object *find_cached_object(const unsigned char *sha1)
 -{
 -      int i;
 -      struct cached_object *co = cached_objects;
 -
 -      for (i = 0; i < cached_object_nr; i++, co++) {
 -              if (!hashcmp(co->sha1, sha1))
 -                      return co;
 -      }
 -      if (!hashcmp(sha1, empty_tree.sha1))
 -              return &empty_tree;
 -      return NULL;
 -}
 -
  int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
                      unsigned char *sha1)
  {
@@@ -2172,48 -2107,27 +2201,48 @@@ static void *read_object(const unsigne
        return read_packed_sha1(sha1, type, size);
  }
  
 +/*
 + * This function dies on corrupt objects; the callers who want to
 + * deal with them should arrange to call read_object() and give error
 + * messages themselves.
 + */
  void *read_sha1_file_repl(const unsigned char *sha1,
                          enum object_type *type,
                          unsigned long *size,
                          const unsigned char **replacement)
  {
        const unsigned char *repl = lookup_replace_object(sha1);
 -      void *data = read_object(repl, type, size);
 +      void *data;
 +      char *path;
 +      const struct packed_git *p;
 +
 +      errno = 0;
 +      data = read_object(repl, type, size);
 +      if (data) {
 +              if (replacement)
 +                      *replacement = repl;
 +              return data;
 +      }
 +
 +      if (errno && errno != ENOENT)
 +              die_errno("failed to read object %s", sha1_to_hex(sha1));
  
        /* die if we replaced an object with one that does not exist */
 -      if (!data && repl != sha1)
 +      if (repl != sha1)
                die("replacement %s not found for %s",
                    sha1_to_hex(repl), sha1_to_hex(sha1));
  
 -      /* legacy behavior is to die on corrupted objects */
 -      if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
 -              die("object %s is corrupted", sha1_to_hex(repl));
 +      if (has_loose_object(repl)) {
 +              path = sha1_file_name(sha1);
 +              die("loose object %s (stored in %s) is corrupt",
 +                  sha1_to_hex(repl), path);
 +      }
  
 -      if (replacement)
 -              *replacement = repl;
 +      if ((p = has_packed_and_bad(repl)) != NULL)
 +              die("packed object %s (stored in %s) is corrupt",
 +                  sha1_to_hex(repl), p->pack_name);
  
 -      return data;
 +      return NULL;
  }
  
  void *read_object_with_reference(const unsigned char *sha1,
@@@ -2393,7 -2307,7 +2422,7 @@@ static int create_tmpfile(char *buffer
  }
  
  static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 -                            void *buf, unsigned long len, time_t mtime)
 +                            const void *buf, unsigned long len, time_t mtime)
  {
        int fd, ret;
        unsigned char compressed[4096];
  
        filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
 -      while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1))
 -              fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
        git_SHA1_Update(&c, hdr, hdrlen);
  
        /* Then the data itself.. */
 -      stream.next_in = buf;
 +      stream.next_in = (void *)buf;
        stream.avail_in = len;
        do {
                unsigned char *in0 = stream.next_in;
        return move_temp_to_file(tmpfile, filename);
  }
  
 -int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 +int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        unsigned char sha1[20];
        char hdr[32];
@@@ -2523,37 -2439,8 +2552,37 @@@ int has_sha1_file(const unsigned char *
        return has_loose_object(sha1);
  }
  
 +static void check_tree(const void *buf, size_t size)
 +{
 +      struct tree_desc desc;
 +      struct name_entry entry;
 +
 +      init_tree_desc(&desc, buf, size);
 +      while (tree_entry(&desc, &entry))
 +              /* do nothing
 +               * tree_entry() will die() on malformed entries */
 +              ;
 +}
 +
 +static void check_commit(const void *buf, size_t size)
 +{
 +      struct commit c;
 +      memset(&c, 0, sizeof(c));
 +      if (parse_commit_buffer(&c, buf, size))
 +              die("corrupt commit");
 +}
 +
 +static void check_tag(const void *buf, size_t size)
 +{
 +      struct tag t;
 +      memset(&t, 0, sizeof(t));
 +      if (parse_tag_buffer(&t, buf, size))
 +              die("corrupt tag");
 +}
 +
  static int index_mem(unsigned char *sha1, void *buf, size_t size,
 -                   int write_object, enum object_type type, const char *path)
 +                   int write_object, enum object_type type,
 +                   const char *path, int format_check)
  {
        int ret, re_allocated = 0;
  
                        re_allocated = 1;
                }
        }
 +      if (format_check) {
 +              if (type == OBJ_TREE)
 +                      check_tree(buf, size);
 +              if (type == OBJ_COMMIT)
 +                      check_commit(buf, size);
 +              if (type == OBJ_TAG)
 +                      check_tag(buf, size);
 +      }
  
        if (write_object)
                ret = write_sha1_file(buf, size, typename(type), sha1);
  #define SMALL_FILE_SIZE (32*1024)
  
  int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
 -           enum object_type type, const char *path)
 +           enum object_type type, const char *path, int format_check)
  {
        int ret;
        size_t size = xsize_t(st->st_size);
                struct strbuf sbuf = STRBUF_INIT;
                if (strbuf_read(&sbuf, fd, 4096) >= 0)
                        ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
 -                                      type, path);
 +                                      type, path, format_check);
                else
                        ret = -1;
                strbuf_release(&sbuf);
 +      } else if (!size) {
 +              ret = index_mem(sha1, NULL, size, write_object, type, path,
 +                              format_check);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, write_object, type,
 -                                      path);
 +                                      path, format_check);
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
 -      } else if (size) {
 +      } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -              ret = index_mem(sha1, buf, size, write_object, type, path);
 +              ret = index_mem(sha1, buf, size, write_object, type, path,
 +                              format_check);
                munmap(buf, size);
 -      } else
 -              ret = index_mem(sha1, NULL, size, write_object, type, path);
 +      }
        close(fd);
        return ret;
  }
@@@ -2637,7 -2513,7 +2666,7 @@@ int index_path(unsigned char *sha1, con
                if (fd < 0)
                        return error("open(\"%s\"): %s", path,
                                     strerror(errno));
 -              if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
 +              if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
@@@ -2676,13 -2552,3 +2705,13 @@@ int read_pack_header(int fd, struct pac
                return PH_ERROR_PROTOCOL;
        return 0;
  }
 +
 +void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
 +{
 +      enum object_type type = sha1_object_info(sha1, NULL);
 +      if (type < 0)
 +              die("%s is not a valid object", sha1_to_hex(sha1));
 +      if (type != expect)
 +              die("%s is not a valid '%s' object", sha1_to_hex(sha1),
 +                  typename(expect));
 +}