Merge branch 'jc/hash-object'
authorJunio C Hamano <gitster@pobox.com>
Mon, 11 May 2015 21:23:59 +0000 (14:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 11 May 2015 21:23:59 +0000 (14:23 -0700)
"hash-object --literally" introduced in v2.2 was not prepared to
take a really long object type name.

* jc/hash-object:
write_sha1_file(): do not use a separate sha1[] array
t1007: add hash-object --literally tests
hash-object --literally: fix buffer overrun with extra-long object type
git-hash-object.txt: document --literally option

1  2 
builtin/hash-object.c
cache.h
sha1_file.c
t/t1007-hash-object.sh
diff --combined builtin/hash-object.c
index 207b90c7b13e103b29305b68aa99d1b9b09342e7,17e8bfdc446f3218124783e5e7a81bdb66079379..07fef3cc6b832c257bd3256dc6fff3d4c51760d9
@@@ -22,10 -22,8 +22,8 @@@ static int hash_literally(unsigned cha
  
        if (strbuf_read(&buf, fd, 4096) < 0)
                ret = -1;
-       else if (flags & HASH_WRITE_OBJECT)
-               ret = write_sha1_file(buf.buf, buf.len, type, sha1);
        else
-               ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
+               ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
        strbuf_release(&buf);
        return ret;
  }
@@@ -79,7 -77,7 +77,7 @@@ static void hash_stdin_paths(const cha
  int cmd_hash_object(int argc, const char **argv, const char *prefix)
  {
        static const char * const hash_object_usage[] = {
 -              N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."),
 +              N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
                N_("git hash-object  --stdin-paths < <list-of-paths>"),
                NULL
        };
diff --combined cache.h
index 771b775621595e312484295abd9ca768ece6f6ea,e037cadf4c43d6b1017587e0de17e7f94b573410..5970940743618fcee3490a2d082b46e934ee2e3d
+++ b/cache.h
@@@ -43,14 -43,6 +43,14 @@@ int git_deflate_end_gently(git_zstream 
  int git_deflate(git_zstream *, int flush);
  unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
 +/* The length in bytes and in hex digits of an object name (SHA-1 value). */
 +#define GIT_SHA1_RAWSZ 20
 +#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 +
 +struct object_id {
 +      unsigned char hash[GIT_SHA1_RAWSZ];
 +};
 +
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
   *
   * The value 0160000 is not normally a valid mode, and
   * also just happens to be S_IFDIR + S_IFLNK
 - *
 - * NOTE! We *really* shouldn't depend on the S_IFxxx macros
 - * always having the same values everywhere. We should use
 - * our internal git values for these things, and then we can
 - * translate that to the OS-specific value. It just so
 - * happens that everybody shares the same bit representation
 - * in the UNIX world (and apparently wider too..)
   */
  #define S_IFGITLINK   0160000
  #define S_ISGITLINK(m)        (((m) & S_IFMT) == S_IFGITLINK)
@@@ -378,7 -377,6 +378,7 @@@ static inline enum object_type object_t
  
  /* Double-check local_repo_env below if you add to this list. */
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
 +#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
@@@ -432,13 -430,11 +432,13 @@@ extern int is_inside_git_dir(void)
  extern char *git_work_tree_cfg;
  extern int is_inside_work_tree(void);
  extern const char *get_git_dir(void);
 +extern const char *get_git_common_dir(void);
  extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
 +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_git_work_tree(void);
@@@ -486,7 -482,7 +486,7 @@@ extern int daemonize(void)
                                alloc = (nr); \
                        else \
                                alloc = alloc_nr(alloc); \
 -                      x = xrealloc((x), alloc * sizeof(*(x))); \
 +                      REALLOC_ARRAY(x, alloc); \
                } \
        } while (0)
  
@@@ -574,12 -570,30 +574,12 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  
 -struct lock_file {
 -      struct lock_file *next;
 -      int fd;
 -      pid_t owner;
 -      char on_list;
 -      char filename[PATH_MAX];
 -};
 -#define LOCK_DIE_ON_ERROR 1
 -#define LOCK_NODEREF 2
 -extern int unable_to_lock_error(const char *path, int err);
 -extern void unable_to_lock_message(const char *path, int err,
 -                                 struct strbuf *buf);
 -extern NORETURN void unable_to_lock_index_die(const char *path, int err);
 -extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 -extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 -extern int commit_lock_file(struct lock_file *);
 -extern int reopen_lock_file(struct lock_file *);
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
 -extern int close_lock_file(struct lock_file *);
 -extern void rollback_lock_file(struct lock_file *);
 -extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
 +
 +extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags);
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -621,17 -635,6 +621,17 @@@ extern int fsync_object_files
  extern int core_preload_index;
  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
 + * generally choke dangerous operations rather than letting
 + * them silently proceed without taking the broken ref into
 + * account.
 + */
 +extern int ref_paranoia;
  
  /*
   * The character that begins a commented line in user-editable file
@@@ -694,19 -697,18 +694,19 @@@ extern int check_repository_format(void
  
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 -extern char *git_snpath(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 char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
  extern char *mkpathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
  
  /* 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, ...)
 +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_path_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +extern void report_linked_checkout_garbage(void);
  
  /*
   * Return the name of the file in the local object database that would
@@@ -731,13 -733,13 +731,13 @@@ extern char *sha1_pack_name(const unsig
  extern char *sha1_pack_index_name(const unsigned char *sha1);
  
  extern const char *find_unique_abbrev(const unsigned char *sha1, int);
 -extern const unsigned char null_sha1[20];
 +extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        int i;
  
 -      for (i = 0; i < 20; i++, sha1++, sha2++) {
 +      for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
                if (*sha1 != *sha2)
                        return *sha1 - *sha2;
        }
        return 0;
  }
  
 +static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
 +{
 +      return hashcmp(oid1->hash, oid2->hash);
 +}
 +
  static inline int is_null_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, null_sha1);
  }
  
 +static inline int is_null_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, null_sha1);
 +}
 +
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
 -      memcpy(sha_dst, sha_src, 20);
 +      memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
 +}
 +
 +static inline void oidcpy(struct object_id *dst, const struct object_id *src)
 +{
 +      hashcpy(dst->hash, src->hash);
  }
 +
  static inline void hashclr(unsigned char *hash)
  {
 -      memset(hash, 0, 20);
 +      memset(hash, 0, GIT_SHA1_RAWSZ);
  }
  
 +static inline void oidclr(struct object_id *oid)
 +{
 +      hashclr(oid->hash);
 +}
 +
 +
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
  #define EMPTY_TREE_SHA1_BIN_LITERAL \
@@@ -869,7 -849,6 +869,7 @@@ int normalize_path_copy(char *dst, cons
  int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
 +extern int is_ntfs_dotgit(const char *name);
  
  /* object replacement */
  #define LOOKUP_REPLACE_OBJECT 1
@@@ -909,6 -888,7 +909,7 @@@ static inline const unsigned char *look
  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);
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
+ 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);
@@@ -987,12 -967,10 +988,12 @@@ extern int for_each_abbrev(const char *
   * null-terminated string.
   */
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 +extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
 -extern int read_ref_full(const char *refname, unsigned char *sha1,
 -                       int reading, int *flags);
 +extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
 +extern int read_ref_full(const char *refname, int resolve_flags,
 +                       unsigned char *sha1, int *flags);
  extern int read_ref(const char *refname, unsigned char *sha1);
  
  /*
   * or the input ref.
   *
   * If the reference cannot be resolved to an object, the behavior
 - * depends on the "reading" argument:
 + * depends on the RESOLVE_REF_READING flag:
   *
 - * - If reading is set, return NULL.
 + * - If RESOLVE_REF_READING is set, return NULL.
   *
 - * - If reading is not set, clear sha1 and return the name of the last
 - *   reference name in the chain, which will either be a non-symbolic
 + * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
 + *   the last reference name in the chain, which will either be a non-symbolic
   *   reference or an undefined reference.  If this is a prelude to
   *   "writing" to the ref, the return value is the name of the ref
   *   that will actually be created or changed.
   *
 - * If flag is non-NULL, set the value that it points to the
 + * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
 + * level of symbolic reference.  The value stored in sha1 for a symbolic
 + * reference will always be null_sha1 in this case, and the return
 + * value is the reference that the symref refers to directly.
 + *
 + * If flags is non-NULL, set the value that it points to the
   * combination of REF_ISPACKED (if the reference was found among the
 - * packed references) and REF_ISSYMREF (if the initial reference was a
 - * symbolic reference).
 + * packed references), REF_ISSYMREF (if the initial reference was a
 + * symbolic reference), REF_BAD_NAME (if the reference name is ill
 + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
 + * (if the ref is malformed or has a bad name). See refs.h for more detail
 + * on each flag.
   *
   * If ref is not a properly-formatted, normalized reference, return
   * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
   * give up and return NULL.
   *
 - * errno is set to something meaningful on error.
 + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
 + * name is invalid according to git-check-ref-format(1).  If the name
 + * is bad then the value stored in sha1 will be null_sha1 and the two
 + * flags REF_ISBROKEN and REF_BAD_NAME will be set.
 + *
 + * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
 + * directory and do not consist of all caps and underscores cannot be
 + * resolved. The function returns NULL for such ref names.
 + * Caps and underscores refers to the special refs, such as HEAD,
 + * FETCH_HEAD and friends, that all live outside of the refs/ directory.
   */
 -extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
 -extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 +#define RESOLVE_REF_READING 0x01
 +#define RESOLVE_REF_NO_RECURSE 0x02
 +#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 +extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 +extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
  
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
@@@ -1082,7 -1040,6 +1083,7 @@@ enum date_mode 
        DATE_SHORT,
        DATE_LOCAL,
        DATE_ISO8601,
 +      DATE_ISO8601_STRICT,
        DATE_RFC2822,
        DATE_RAW
  };
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
  void show_date_relative(unsigned long time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
 -int parse_date(const char *date, char *buf, int bufsize);
 +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);
 -void datestamp(char *buf, int bufsize);
 +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);
@@@ -1186,7 -1143,7 +1187,7 @@@ extern void prepare_alt_odb(void)
  extern void read_info_alternates(const char * relative_base, int depth);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
 -extern void foreach_alt_odb(alt_odb_fn, void*);
 +extern int foreach_alt_odb(alt_odb_fn, void*);
  
  struct pack_window {
        struct pack_window *next;
@@@ -1211,7 -1168,6 +1212,7 @@@ extern struct packed_git 
        int pack_fd;
        unsigned pack_local:1,
                 pack_keep:1,
 +               freshened:1,
                 do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
@@@ -1283,61 -1239,6 +1284,61 @@@ extern unsigned long unpack_object_head
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
  extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
  
 +/*
 + * Iterate over the files in the loose-object parts of the object
 + * directory "path", triggering the following callbacks:
 + *
 + *  - loose_object is called for each loose object we find.
 + *
 + *  - loose_cruft is called for any files that do not appear to be
 + *    loose objects. Note that we only look in the loose object
 + *    directories "objects/[0-9a-f]{2}/", so we will not report
 + *    "objects/foobar" as cruft.
 + *
 + *  - loose_subdir is called for each top-level hashed subdirectory
 + *    of the object directory (e.g., "$OBJDIR/f0"). It is called
 + *    after the objects in the directory are processed.
 + *
 + * Any callback that is NULL will be ignored. Callbacks returning non-zero
 + * will end the iteration.
 + *
 + * In the "buf" variant, "path" is a strbuf which will also be used as a
 + * scratch buffer, but restored to its original contents before
 + * the function returns.
 + */
 +typedef int each_loose_object_fn(const unsigned char *sha1,
 +                               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,
 +                               const char *path,
 +                               void *data);
 +int for_each_loose_file_in_objdir(const char *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_buf(struct strbuf *path,
 +                                    each_loose_object_fn obj_cb,
 +                                    each_loose_cruft_fn cruft_cb,
 +                                    each_loose_subdir_fn subdir_cb,
 +                                    void *data);
 +
 +/*
 + * Iterate over loose and packed objects in both the local
 + * repository and any alternates repositories (unless the
 + * LOCAL_ONLY flag is set).
 + */
 +#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 +typedef int each_packed_object_fn(const unsigned char *sha1,
 +                                struct packed_git *pack,
 +                                uint32_t pos,
 +                                void *data);
 +extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 +extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
 +
  struct object_info {
        /* Request */
        enum object_type *typep;
@@@ -1384,8 -1285,6 +1385,8 @@@ extern int update_server_info(int)
  #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;
@@@ -1423,7 -1322,6 +1424,7 @@@ extern int git_config_rename_section_in
  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 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__)
@@@ -1530,8 -1428,6 +1531,8 @@@ extern const char *git_mailmap_blob
  
  /* IO helper functions */
  extern void maybe_flush_or_die(FILE *, const char *);
 +__attribute__((format (printf, 2, 3)))
 +extern void fprintf_or_die(FILE *, const char *fmt, ...);
  extern int copy_fd(int ifd, int ofd);
  extern int copy_file(const char *dst, const char *src, int mode);
  extern int copy_file_with_time(const char *dst, const char *src, int mode);
@@@ -1548,8 -1444,6 +1549,8 @@@ static inline ssize_t write_str_in_full
  {
        return write_in_full(fd, str, strlen(str));
  }
 +__attribute__((format (printf, 3, 4)))
 +extern int write_file(const char *path, int fatal, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
@@@ -1557,7 -1451,7 +1558,7 @@@ extern const char *pager_program
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
 -extern int decimal_width(int);
 +extern int decimal_width(uintmax_t);
  extern int check_pager_config(const char *cmd);
  
  extern const char *editor_program;
@@@ -1619,6 -1513,7 +1620,6 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined sha1_file.c
index 7634917e4ea934f6673d6d154420499660dbc1ab,5d320753c3eb57ab3c669e4fa945ba87c993fe6b..866021b2b10ff228a391d38342d7352b1ea779ff
@@@ -8,7 -8,6 +8,7 @@@
   */
  #include "cache.h"
  #include "string-list.h"
 +#include "lockfile.h"
  #include "delta.h"
  #include "pack.h"
  #include "blob.h"
@@@ -405,7 -404,7 +405,7 @@@ void add_to_alternates_file(const char 
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
 -      char *alt = mkpath("%s\n", reference);
 +      const char *alt = mkpath("%s\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
                die("could not close alternates file");
                link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
  }
  
 -void foreach_alt_odb(alt_odb_fn fn, void *cb)
 +int foreach_alt_odb(alt_odb_fn fn, void *cb)
  {
        struct alternate_object_database *ent;
 +      int r = 0;
  
        prepare_alt_odb();
 -      for (ent = alt_odb_list; ent; ent = ent->next)
 -              if (fn(ent, cb))
 -                      return;
 +      for (ent = alt_odb_list; ent; ent = ent->next) {
 +              r = fn(ent, cb);
 +              if (r)
 +                      break;
 +      }
 +      return r;
  }
  
  void prepare_alt_odb(void)
        read_info_alternates(get_object_directory(), 0);
  }
  
 -static int has_loose_object_local(const unsigned char *sha1)
 +static int freshen_file(const char *fn)
  {
 -      return !access(sha1_file_name(sha1), F_OK);
 +      struct utimbuf t;
 +      t.actime = t.modtime = time(NULL);
 +      return !utime(fn, &t);
  }
  
 -int has_loose_object_nonlocal(const unsigned char *sha1)
 +static int check_and_freshen_file(const char *fn, int freshen)
 +{
 +      if (access(fn, F_OK))
 +              return 0;
 +      if (freshen && freshen_file(fn))
 +              return 0;
 +      return 1;
 +}
 +
 +static int check_and_freshen_local(const unsigned char *sha1, int freshen)
 +{
 +      return check_and_freshen_file(sha1_file_name(sha1), freshen);
 +}
 +
 +static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
  {
        struct alternate_object_database *alt;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
                fill_sha1_path(alt->name, sha1);
 -              if (!access(alt->base, F_OK))
 +              if (check_and_freshen_file(alt->base, freshen))
                        return 1;
        }
        return 0;
  }
  
 +static int check_and_freshen(const unsigned char *sha1, int freshen)
 +{
 +      return check_and_freshen_local(sha1, freshen) ||
 +             check_and_freshen_nonlocal(sha1, freshen);
 +}
 +
 +int has_loose_object_nonlocal(const unsigned char *sha1)
 +{
 +      return check_and_freshen_nonlocal(sha1, 0);
 +}
 +
  static int has_loose_object(const unsigned char *sha1)
  {
 -      return has_loose_object_local(sha1) ||
 -             has_loose_object_nonlocal(sha1);
 +      return check_and_freshen(sha1, 0);
  }
  
  static unsigned int pack_used_ctr;
@@@ -694,26 -663,10 +694,26 @@@ void release_pack_memory(size_t need
                ; /* nothing */
  }
  
 +static void mmap_limit_check(size_t length)
 +{
 +      static size_t limit = 0;
 +      if (!limit) {
 +              limit = git_env_ulong("GIT_MMAP_LIMIT", 0);
 +              if (!limit)
 +                      limit = SIZE_MAX;
 +      }
 +      if (length > limit)
 +              die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX,
 +                  (uintmax_t)length, (uintmax_t)limit);
 +}
 +
  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);
 +      void *ret;
 +
 +      mmap_limit_check(length);
 +      ret = mmap(start, length, prot, flags, fd, offset);
        if (ret == MAP_FAILED) {
                if (!length)
                        return NULL;
@@@ -1198,7 -1151,7 +1198,7 @@@ static void report_pack_garbage(struct 
        if (!report_garbage)
                return;
  
 -      sort_string_list(list);
 +      string_list_sort(list);
  
        for (i = 0; i < list->nr; i++) {
                const char *path = list->items[i].string;
@@@ -2473,8 -2426,10 +2473,8 @@@ static int fill_pack_entry(const unsign
         * answer, as it may have been deleted since the index was
         * loaded!
         */
 -      if (!is_pack_valid(p)) {
 -              warning("packfile %s cannot be accessed", p->pack_name);
 +      if (!is_pack_valid(p))
                return 0;
 -      }
        e->offset = offset;
        e->p = p;
        hashcpy(e->sha1, sha1);
@@@ -2941,6 -2896,7 +2941,6 @@@ static int write_loose_object(const uns
        }
  
        /* Set it up */
 -      memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        stream.next_out = compressed;
        stream.avail_out = sizeof(compressed);
        return move_temp_to_file(tmp_file, filename);
  }
  
- int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 +static int freshen_loose_object(const unsigned char *sha1)
 +{
 +      return check_and_freshen(sha1, 1);
 +}
 +
 +static int freshen_packed_object(const unsigned char *sha1)
 +{
 +      struct pack_entry e;
 +      if (!find_pack_entry(sha1, &e))
 +              return 0;
 +      if (e.p->freshened)
 +              return 1;
 +      if (!freshen_file(e.p->pack_name))
 +              return 0;
 +      e.p->freshened = 1;
 +      return 1;
 +}
 +
+ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)
  {
-       unsigned char sha1[20];
        char hdr[32];
        int hdrlen;
  
         * it out into .git/objects/??/?{38} file.
         */
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
-       if (returnsha1)
-               hashcpy(returnsha1, sha1);
 -      if (has_sha1_file(sha1))
 +      if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
  }
  
 -      if (has_sha1_file(sha1))
+ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
+                            unsigned char *sha1, unsigned flags)
+ {
+       char *header;
+       int hdrlen, status = 0;
+       /* type string, SP, %lu of the length plus NUL must fit this */
+       header = xmalloc(strlen(type) + 32);
+       write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
+       if (!(flags & HASH_WRITE_OBJECT))
+               goto cleanup;
++      if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
+               goto cleanup;
+       status = write_loose_object(sha1, header, hdrlen, buf, len, 0);
+ cleanup:
+       free(header);
+       return status;
+ }
  int force_object_loose(const unsigned char *sha1, time_t mtime)
  {
        void *buf;
@@@ -3138,29 -3094,6 +3156,29 @@@ static int index_mem(unsigned char *sha
        return ret;
  }
  
 +static int index_stream_convert_blob(unsigned char *sha1, int fd,
 +                                   const char *path, unsigned flags)
 +{
 +      int ret;
 +      const int write_object = flags & HASH_WRITE_OBJECT;
 +      struct strbuf sbuf = STRBUF_INIT;
 +
 +      assert(path);
 +      assert(would_convert_to_git_filter_fd(path));
 +
 +      convert_to_git_filter_fd(path, fd, &sbuf,
 +                               write_object ? safe_crlf : SAFE_CRLF_FALSE);
 +
 +      if (write_object)
 +              ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 +                                    sha1);
 +      else
 +              ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 +                                   sha1);
 +      strbuf_release(&sbuf);
 +      return ret;
 +}
 +
  static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
                      const char *path, unsigned flags)
  {
@@@ -3226,22 -3159,15 +3244,22 @@@ int index_fd(unsigned char *sha1, int f
             enum object_type type, const char *path, unsigned flags)
  {
        int ret;
 -      size_t size = xsize_t(st->st_size);
  
 -      if (!S_ISREG(st->st_mode))
 +      /*
 +       * Call xsize_t() only when needed to avoid potentially unnecessary
 +       * die() for large files.
 +       */
 +      if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
 +              ret = index_stream_convert_blob(sha1, fd, path, flags);
 +      else if (!S_ISREG(st->st_mode))
                ret = index_pipe(sha1, fd, type, path, flags);
 -      else if (size <= big_file_threshold || type != OBJ_BLOB ||
 -               (path && would_convert_to_git(path, NULL, 0, 0)))
 -              ret = index_core(sha1, fd, size, type, path, flags);
 +      else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
 +               (path && would_convert_to_git(path)))
 +              ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
 +                               flags);
        else
 -              ret = index_stream(sha1, fd, size, type, path, flags);
 +              ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path,
 +                                 flags);
        close(fd);
        return ret;
  }
@@@ -3306,172 -3232,3 +3324,172 @@@ void assert_sha1_type(const unsigned ch
                die("%s is not a valid '%s' object", sha1_to_hex(sha1),
                    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)
 +{
 +      size_t baselen = path->len;
 +      DIR *dir = opendir(path->buf);
 +      struct dirent *de;
 +      int r = 0;
 +
 +      if (!dir) {
 +              if (errno == ENOENT)
 +                      return 0;
 +              return error("unable to open %s: %s", path->buf, strerror(errno));
 +      }
 +
 +      while ((de = readdir(dir))) {
 +              if (is_dot_or_dotdot(de->d_name))
 +                      continue;
 +
 +              strbuf_setlen(path, baselen);
 +              strbuf_addf(path, "/%s", de->d_name);
 +
 +              if (strlen(de->d_name) == 38)  {
 +                      char hex[41];
 +                      unsigned char sha1[20];
 +
 +                      snprintf(hex, sizeof(hex), "%02x%s",
 +                               subdir_nr, de->d_name);
 +                      if (!get_sha1_hex(hex, sha1)) {
 +                              if (obj_cb) {
 +                                      r = obj_cb(sha1, path->buf, data);
 +                                      if (r)
 +                                              break;
 +                              }
 +                              continue;
 +                      }
 +              }
 +
 +              if (cruft_cb) {
 +                      r = cruft_cb(de->d_name, path->buf, data);
 +                      if (r)
 +                              break;
 +              }
 +      }
 +      strbuf_setlen(path, baselen);
 +
 +      if (!r && subdir_cb)
 +              r = subdir_cb(subdir_nr, path->buf, data);
 +
 +      closedir(dir);
 +      return r;
 +}
 +
 +int for_each_loose_file_in_objdir_buf(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;
 +      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;
 +      }
 +
 +      return r;
 +}
 +
 +int for_each_loose_file_in_objdir(const char *path,
 +                                each_loose_object_fn obj_cb,
 +                                each_loose_cruft_fn cruft_cb,
 +                                each_loose_subdir_fn subdir_cb,
 +                                void *data)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      int r;
 +
 +      strbuf_addstr(&buf, path);
 +      r = for_each_loose_file_in_objdir_buf(&buf, obj_cb, cruft_cb,
 +                                            subdir_cb, data);
 +      strbuf_release(&buf);
 +
 +      return r;
 +}
 +
 +struct loose_alt_odb_data {
 +      each_loose_object_fn *cb;
 +      void *data;
 +};
 +
 +static int loose_from_alt_odb(struct alternate_object_database *alt,
 +                            void *vdata)
 +{
 +      struct loose_alt_odb_data *data = vdata;
 +      struct strbuf buf = STRBUF_INIT;
 +      int r;
 +
 +      /* copy base not including trailing '/' */
 +      strbuf_add(&buf, alt->base, alt->name - alt->base - 1);
 +      r = for_each_loose_file_in_objdir_buf(&buf,
 +                                            data->cb, NULL, NULL,
 +                                            data->data);
 +      strbuf_release(&buf);
 +      return r;
 +}
 +
 +int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags)
 +{
 +      struct loose_alt_odb_data alt;
 +      int r;
 +
 +      r = for_each_loose_file_in_objdir(get_object_directory(),
 +                                        cb, NULL, NULL, data);
 +      if (r)
 +              return r;
 +
 +      if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
 +              return 0;
 +
 +      alt.cb = cb;
 +      alt.data = data;
 +      return foreach_alt_odb(loose_from_alt_odb, &alt);
 +}
 +
 +static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
 +{
 +      uint32_t i;
 +      int r = 0;
 +
 +      for (i = 0; i < p->num_objects; i++) {
 +              const unsigned char *sha1 = nth_packed_object_sha1(p, i);
 +
 +              if (!sha1)
 +                      return error("unable to get sha1 of object %u in %s",
 +                                   i, p->pack_name);
 +
 +              r = cb(sha1, p, i, data);
 +              if (r)
 +                      break;
 +      }
 +      return r;
 +}
 +
 +int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
 +{
 +      struct packed_git *p;
 +      int r = 0;
 +
 +      prepare_packed_git();
 +      for (p = packed_git; p; p = p->next) {
 +              if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
 +                      continue;
 +              r = for_each_object_in_pack(p, cb, data);
 +              if (r)
 +                      break;
 +      }
 +      return r;
 +}
diff --combined t/t1007-hash-object.sh
index ebb3a69c8cd68a51184f809357bb60cb7b35f928,7c3dcfb70ccb32314eb0a95edb0d2522936f48dd..7d2baa15bbd0bbbe8c12631ad2d24b77e90b8775
@@@ -201,12 -201,15 +201,23 @@@ test_expect_success 'corrupt tag' 
        test_must_fail git hash-object -t tag --stdin </dev/null
  '
  
 +test_expect_success 'hash-object complains about bogus type name' '
 +      test_must_fail git hash-object -t bogus --stdin </dev/null
 +'
 +
 +test_expect_success 'hash-object complains about truncated type name' '
 +      test_must_fail git hash-object -t bl --stdin </dev/null
 +'
 +
+ test_expect_success '--literally' '
+       t=1234567890 &&
+       echo example | git hash-object -t $t --literally --stdin
+ '
+ test_expect_success '--literally with extra-long type' '
+       t=12345678901234567890123456789012345678901234567890 &&
+       t="$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t" &&
+       echo example | git hash-object -t $t --literally --stdin
+ '
  test_done