Merge branch 'js/misc-fixes'
authorJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:06:59 +0000 (13:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:07:00 +0000 (13:07 -0700)
Various compilation fixes and squelching of warnings.

* js/misc-fixes:
Correct fscanf formatting string for I64u values
Silence GCC's "cast of pointer to integer of a different size" warning
Squelch warning about an integer overflow

1  2 
builtin/gc.c
git-compat-util.h
sha1_file.c
diff --combined builtin/gc.c
index b677923ffc2eec4b12ee9253ca4849ec063daa10,9023aee23ce0174553a3e4b9ecf71b7de2c8679b..df3e454447ea4e4e34c6a7eac7ef54df1d614df6
@@@ -11,7 -11,6 +11,7 @@@
   */
  
  #include "builtin.h"
 +#include "tempfile.h"
  #include "lockfile.h"
  #include "parse-options.h"
  #include "run-command.h"
@@@ -22,7 -21,7 +22,7 @@@
  #define FAILED_RUN "failed to run %s"
  
  static const char * const builtin_gc_usage[] = {
 -      N_("git gc [options]"),
 +      N_("git gc [<options>]"),
        NULL
  };
  
@@@ -34,47 -33,24 +34,47 @@@ static int gc_auto_threshold = 6700
  static int gc_auto_pack_limit = 50;
  static int detach_auto = 1;
  static const char *prune_expire = "2.weeks.ago";
 +static const char *prune_worktrees_expire = "3.months.ago";
  
  static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
  static struct argv_array reflog = ARGV_ARRAY_INIT;
  static struct argv_array repack = ARGV_ARRAY_INIT;
  static struct argv_array prune = ARGV_ARRAY_INIT;
 +static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
  static struct argv_array rerere = ARGV_ARRAY_INIT;
  
 -static char *pidfile;
 +static struct tempfile pidfile;
 +static struct lock_file log_lock;
  
 -static void remove_pidfile(void)
 +static void git_config_date_string(const char *key, const char **output)
  {
 -      if (pidfile)
 -              unlink(pidfile);
 +      if (git_config_get_string_const(key, output))
 +              return;
 +      if (strcmp(*output, "now")) {
 +              unsigned long now = approxidate("now");
 +              if (approxidate(*output) >= now)
 +                      git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 +      }
  }
  
 -static void remove_pidfile_on_signal(int signo)
 +static void process_log_file(void)
  {
 -      remove_pidfile();
 +      struct stat st;
 +      if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
 +              commit_lock_file(&log_lock);
 +      else
 +              rollback_lock_file(&log_lock);
 +}
 +
 +static void process_log_file_at_exit(void)
 +{
 +      fflush(stderr);
 +      process_log_file();
 +}
 +
 +static void process_log_file_on_signal(int signo)
 +{
 +      process_log_file();
        sigchain_pop(signo);
        raise(signo);
  }
@@@ -95,8 -71,16 +95,8 @@@ static void gc_config(void
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
 -
 -      if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) {
 -              if (strcmp(prune_expire, "now")) {
 -                      unsigned long now = approxidate("now");
 -                      if (approxidate(prune_expire) >= now) {
 -                              git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"),
 -                                              prune_expire);
 -                      }
 -              }
 -      }
 +      git_config_date_string("gc.pruneexpire", &prune_expire);
 +      git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
        git_config(git_default_config, NULL);
  }
  
@@@ -210,22 -194,20 +210,22 @@@ static const char *lock_repo_for_gc(in
        uintmax_t pid;
        FILE *fp;
        int fd;
 +      char *pidfile_path;
  
 -      if (pidfile)
 +      if (is_tempfile_active(&pidfile))
                /* already locked */
                return NULL;
  
        if (gethostname(my_host, sizeof(my_host)))
 -              strcpy(my_host, "unknown");
 +              xsnprintf(my_host, sizeof(my_host), "unknown");
  
 -      fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
 +      pidfile_path = git_pathdup("gc.pid");
 +      fd = hold_lock_file_for_update(&lock, pidfile_path,
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
                static char locking_host[128];
                int should_exit;
 -              fp = fopen(git_path("gc.pid"), "r");
 +              fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
                        fp != NULL &&
                         * running.
                         */
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
-                       fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
+                       fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
                        (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
                        if (fd >= 0)
                                rollback_lock_file(&lock);
                        *ret_pid = pid;
 +                      free(pidfile_path);
                        return locking_host;
                }
        }
        write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
        commit_lock_file(&lock);
 -
 -      pidfile = git_pathdup("gc.pid");
 -      sigchain_push_common(remove_pidfile_on_signal);
 -      atexit(remove_pidfile);
 -
 +      register_tempfile(&pidfile, pidfile_path);
 +      free(pidfile_path);
        return NULL;
  }
  
 +static int report_last_gc_error(void)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret;
 +
 +      ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
 +      if (ret > 0)
 +              return error(_("The last gc run reported the following. "
 +                             "Please correct the root cause\n"
 +                             "and remove %s.\n"
 +                             "Automatic cleanup will not be performed "
 +                             "until the file is removed.\n\n"
 +                             "%s"),
 +                           git_path("gc.log"), sb.buf);
 +      strbuf_release(&sb);
 +      return 0;
 +}
 +
  static int gc_before_repack(void)
  {
        if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
@@@ -303,7 -269,6 +303,7 @@@ int cmd_gc(int argc, const char **argv
        int force = 0;
        const char *name;
        pid_t pid;
 +      int daemonized = 0;
  
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, N_("suppress progress reporting")),
        argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
        argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
        argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
 -      argv_array_pushl(&prune, "prune", "--expire", NULL );
 +      argv_array_pushl(&prune, "prune", "--expire", NULL);
 +      argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
        argv_array_pushl(&rerere, "rerere", "gc", NULL);
  
        gc_config();
                        fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
                }
                if (detach_auto) {
 +                      if (report_last_gc_error())
 +                              return -1;
 +
                        if (gc_before_repack())
                                return -1;
                        /*
                         * failure to daemonize is ok, we'll continue
                         * in foreground
                         */
 -                      daemonize();
 +                      daemonized = !daemonize();
                }
        } else
                add_repack_all_option();
                    name, (uintmax_t)pid);
        }
  
 +      if (daemonized) {
 +              hold_lock_file_for_update(&log_lock,
 +                                        git_path("gc.log"),
 +                                        LOCK_DIE_ON_ERROR);
 +              dup2(get_lock_file_fd(&log_lock), 2);
 +              sigchain_push_common(process_log_file_on_signal);
 +              atexit(process_log_file_at_exit);
 +      }
 +
        if (gc_before_repack())
                return -1;
  
 -      if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
 -              return error(FAILED_RUN, repack.argv[0]);
 +      if (!repository_format_precious_objects) {
 +              if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
 +                      return error(FAILED_RUN, repack.argv[0]);
 +
 +              if (prune_expire) {
 +                      argv_array_push(&prune, prune_expire);
 +                      if (quiet)
 +                              argv_array_push(&prune, "--no-progress");
 +                      if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
 +                              return error(FAILED_RUN, prune.argv[0]);
 +              }
 +      }
  
 -      if (prune_expire) {
 -              argv_array_push(&prune, prune_expire);
 -              if (quiet)
 -                      argv_array_push(&prune, "--no-progress");
 -              if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
 -                      return error(FAILED_RUN, prune.argv[0]);
 +      if (prune_worktrees_expire) {
 +              argv_array_push(&prune_worktrees, prune_worktrees_expire);
 +              if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
 +                      return error(FAILED_RUN, prune_worktrees.argv[0]);
        }
  
        if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
diff --combined git-compat-util.h
index 88964f7886b1f6d7445b77a26eceb65d48c3b6b8,33bb76229668f79cd1d4406abbb60d35717f6c84..8e3986791d13a5e9196eea95761f35b5dc2078da
@@@ -3,23 -3,6 +3,23 @@@
  
  #define _FILE_OFFSET_BITS 64
  
 +
 +/* Derived from Linux "Features Test Macro" header
 + * Convenience macros to test the versions of gcc (or
 + * a compatible compiler).
 + * Use them like this:
 + *  #if GIT_GNUC_PREREQ (2,8)
 + *   ... code requiring gcc 2.8 or later ...
 + *  #endif
 +*/
 +#if defined(__GNUC__) && defined(__GNUC_MINOR__)
 +# define GIT_GNUC_PREREQ(maj, min) \
 +      ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
 +#else
 + #define GIT_GNUC_PREREQ(maj, min) 0
 +#endif
 +
 +
  #ifndef FLEX_ARRAY
  /*
   * See if our compiler is known to support flexible array members.
  #endif
  #endif
  
 -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 +
 +/*
 + * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
 + * @cond: the compile-time condition which must be true.
 + *
 + * Your compile will fail if the condition isn't true, or can't be evaluated
 + * by the compiler.  This can be used in an expression: its value is "0".
 + *
 + * Example:
 + *    #define foo_to_char(foo)                                        \
 + *             ((char *)(foo)                                         \
 + *              + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
 + */
 +#define BUILD_ASSERT_OR_ZERO(cond) \
 +      (sizeof(char [1 - 2*!(cond)]) - 1)
 +
 +#if GIT_GNUC_PREREQ(3, 1)
 + /* &arr[0] degrades to a pointer: a different type from an array */
 +# define BARF_UNLESS_AN_ARRAY(arr)                                            \
 +      BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
 +                                                         __typeof__(&(arr)[0])))
 +#else
 +# define BARF_UNLESS_AN_ARRAY(arr) 0
 +#endif
 +/*
 + * ARRAY_SIZE - get the number of elements in a visible array
 + *  <at> x: the array whose size you want.
 + *
 + * This does not work on pointers, or arrays declared as [], or
 + * function parameters.  With correct compiler support, such usage
 + * will cause a build error (see the build_assert_or_zero macro).
 + */
 +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]) + BARF_UNLESS_AN_ARRAY(x))
 +
  #define bitsizeof(x)  (CHAR_BIT * sizeof(x))
  
  #define maximum_signed_value_of_type(a) \
  # endif
  #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
        !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
 -      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
 +      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
 +      !defined(__CYGWIN__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
  #endif
  #else
  #include <poll.h>
  #endif
 +#ifdef HAVE_BSD_SYSCTL
 +#include <sys/sysctl.h>
 +#endif
  
  #if defined(__MINGW32__)
  /* pull in Windows compatibility stuff */
  #elif defined(_MSC_VER)
  #include "compat/msvc.h"
  #else
 +#include <sys/utsname.h>
  #include <sys/wait.h>
  #include <sys/resource.h>
  #include <sys/socket.h>
  typedef long intptr_t;
  typedef unsigned long uintptr_t;
  #endif
 -#if defined(__CYGWIN__)
 -#undef _XOPEN_SOURCE
 -#include <grp.h>
 -#define _XOPEN_SOURCE 600
 -#else
  #undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
  #include <grp.h>
  #define _ALL_SOURCE 1
  #endif
 -#endif
  
  /* used on Mac OS X */
  #ifdef PRECOMPOSE_UNICODE
  #else
  #define precompose_str(in,i_nfd2nfc)
  #define precompose_argv(c,v)
 -#define probe_utf8_pathname_composition(a,b)
 +#define probe_utf8_pathname_composition()
  #endif
  
  #ifdef MKDIR_WO_TRAILING_SLASH
@@@ -260,18 -211,8 +260,18 @@@ extern char *gitbasename(char *)
  #endif
  
  #ifndef NO_OPENSSL
 +#ifdef __APPLE__
 +#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0
 +#include <AvailabilityMacros.h>
 +#undef DEPRECATED_ATTRIBUTE
 +#define DEPRECATED_ATTRIBUTE
 +#undef __AVAILABILITY_MACROS_USES_AVAILABILITY
 +#endif
  #include <openssl/ssl.h>
  #include <openssl/err.h>
 +#ifdef NO_HMAC_CTX_CLEANUP
 +#define HMAC_CTX_cleanup HMAC_cleanup
 +#endif
  #endif
  
  /* On most systems <netdb.h> would have given us this, but
  #define PRIuMAX "llu"
  #endif
  
+ #ifndef SCNuMAX
+ #define SCNuMAX PRIuMAX
+ #endif
  #ifndef PRIu32
  #define PRIu32 "u"
  #endif
@@@ -389,6 -334,7 +393,6 @@@ struct strbuf
  
  /* General helper functions */
  extern void vreportf(const char *prefix, const char *err, va_list params);
 -extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
  extern NORETURN void usage(const char *err);
  extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
  extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@@ -424,7 -370,6 +428,7 @@@ static inline int const_error(void
  extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
  extern void set_error_routine(void (*routine)(const char *err, va_list params));
  extern void set_die_is_recursing_routine(int (*routine)(void));
 +extern void set_error_handle(FILE *);
  
  extern int starts_with(const char *str, const char *prefix);
  
@@@ -533,42 -478,8 +537,42 @@@ extern int git_munmap(void *start, size
  #define on_disk_bytes(st) ((st).st_blocks * 512)
  #endif
  
 +#ifdef NEEDS_MODE_TRANSLATION
 +#undef S_IFMT
 +#undef S_IFREG
 +#undef S_IFDIR
 +#undef S_IFLNK
 +#undef S_IFBLK
 +#undef S_IFCHR
 +#undef S_IFIFO
 +#undef S_IFSOCK
 +#define S_IFMT   0170000
 +#define S_IFREG  0100000
 +#define S_IFDIR  0040000
 +#define S_IFLNK  0120000
 +#define S_IFBLK  0060000
 +#define S_IFCHR  0020000
 +#define S_IFIFO  0010000
 +#define S_IFSOCK 0140000
 +#ifdef stat
 +#undef stat
 +#endif
 +#define stat(path, buf) git_stat(path, buf)
 +extern int git_stat(const char *, struct stat *);
 +#ifdef fstat
 +#undef fstat
 +#endif
 +#define fstat(fd, buf) git_fstat(fd, buf)
 +extern int git_fstat(int, struct stat *);
 +#ifdef lstat
 +#undef lstat
 +#endif
 +#define lstat(path, buf) git_lstat(path, buf)
 +extern int git_lstat(const char *, struct stat *);
 +#endif
 +
  #define DEFAULT_PACKED_GIT_LIMIT \
-       ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
+       ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256))
  
  #ifdef NO_PREAD
  #define pread git_pread
@@@ -716,13 -627,10 +720,13 @@@ extern char *xstrndup(const char *str, 
  extern void *xrealloc(void *ptr, size_t size);
  extern void *xcalloc(size_t nmemb, size_t size);
  extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 +extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 +extern int xopen(const char *path, int flags, ...);
  extern ssize_t xread(int fd, void *buf, size_t len);
  extern ssize_t xwrite(int fd, const void *buf, size_t len);
  extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
  extern int xdup(int fd);
 +extern FILE *xfopen(const char *path, const char *mode);
  extern FILE *xfdopen(int fd, const char *mode);
  extern int xmkstemp(char *template);
  extern int xmkstemp_mode(char *template, int mode);
@@@ -732,11 -640,6 +736,11 @@@ extern char *xgetcwd(void)
  
  #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x)))
  
 +static inline char *xstrdup_or_null(const char *str)
 +{
 +      return str ? xstrdup(str) : NULL;
 +}
 +
  static inline size_t xsize_t(off_t len)
  {
        if (len > (size_t) len)
        return (size_t)len;
  }
  
 +__attribute__((format (printf, 3, 4)))
 +extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
 +
  /* in ctype.c, for kwset users */
 -extern const char tolower_trans_tbl[256];
 +extern const unsigned char tolower_trans_tbl[256];
  
  /* Sane ctype - no locale, and works with signed chars */
  #undef isascii
@@@ -817,9 -717,6 +821,9 @@@ static inline int strtoul_ui(char cons
        char *p;
  
        errno = 0;
 +      /* negative values would be accepted by strtoul */
 +      if (strchr(s, '-'))
 +              return -1;
        ul = strtoul(s, &p, base);
        if (errno || *p || p == s || (unsigned int) ul != ul)
                return -1;
@@@ -935,18 -832,4 +939,18 @@@ struct tm *git_gmtime_r(const time_t *
  #define gmtime_r git_gmtime_r
  #endif
  
 +#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
 +#define USE_PARENS_AROUND_GETTEXT_N 1
 +#endif
 +
 +#ifndef SHELL_PATH
 +# define SHELL_PATH "/bin/sh"
 +#endif
 +
 +#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
 +#define flockfile(fh)
 +#define funlockfile(fh)
 +#define getc_unlocked(fh) getc(fh)
 +#endif
 +
  #endif
diff --combined sha1_file.c
index 50896ff1eb5ce3f216b31658b4a03d6e7fdc5adb,2350a91dbe5dc9e895e3e688e546ff83307ad6dc..c5b31de9aa579dde37e5345d207995416f261eed
@@@ -208,25 -208,44 +208,25 @@@ const char *sha1_file_name(const unsign
   * provided by the caller.  which should be "pack" or "idx".
   */
  static char *sha1_get_pack_name(const unsigned char *sha1,
 -                              char **name, char **base, const char *which)
 +                              struct strbuf *buf,
 +                              const char *which)
  {
 -      static const char hex[] = "0123456789abcdef";
 -      char *buf;
 -      int i;
 -
 -      if (!*base) {
 -              const char *sha1_file_directory = get_object_directory();
 -              int len = strlen(sha1_file_directory);
 -              *base = xmalloc(len + 60);
 -              sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s",
 -                      sha1_file_directory, which);
 -              *name = *base + len + 11;
 -      }
 -
 -      buf = *name;
 -
 -      for (i = 0; i < 20; i++) {
 -              unsigned int val = *sha1++;
 -              *buf++ = hex[val >> 4];
 -              *buf++ = hex[val & 0xf];
 -      }
 -
 -      return *base;
 +      strbuf_reset(buf);
 +      strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
 +                  sha1_to_hex(sha1), which);
 +      return buf->buf;
  }
  
  char *sha1_pack_name(const unsigned char *sha1)
  {
 -      static char *name, *base;
 -
 -      return sha1_get_pack_name(sha1, &name, &base, "pack");
 +      static struct strbuf buf = STRBUF_INIT;
 +      return sha1_get_pack_name(sha1, &buf, "pack");
  }
  
  char *sha1_pack_index_name(const unsigned char *sha1)
  {
 -      static char *name, *base;
 -
 -      return sha1_get_pack_name(sha1, &name, &base, "idx");
 +      static struct strbuf buf = STRBUF_INIT;
 +      return sha1_get_pack_name(sha1, &buf, "idx");
  }
  
  struct alternate_object_database *alt_odb_list;
@@@ -382,46 -401,13 +382,46 @@@ void read_info_alternates(const char * 
  void add_to_alternates_file(const char *reference)
  {
        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);
 -      write_or_die(fd, alt, strlen(alt));
 -      if (commit_lock_file(lock))
 -              die("could not close alternates file");
 -      if (alt_odb_tail)
 -              link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
 +      char *alts = git_pathdup("objects/info/alternates");
 +      FILE *in, *out;
 +
 +      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(lock, "w");
 +      if (!out)
 +              die_errno("unable to fdopen alternates lockfile");
 +
 +      in = fopen(alts, "r");
 +      if (in) {
 +              struct strbuf line = STRBUF_INIT;
 +              int found = 0;
 +
 +              while (strbuf_getline(&line, in, '\n') != EOF) {
 +                      if (!strcmp(reference, line.buf)) {
 +                              found = 1;
 +                              break;
 +                      }
 +                      fprintf_or_die(out, "%s\n", line.buf);
 +              }
 +
 +              strbuf_release(&line);
 +              fclose(in);
 +
 +              if (found) {
 +                      rollback_lock_file(lock);
 +                      lock = NULL;
 +              }
 +      }
 +      else if (errno != ENOENT)
 +              die_errno("unable to read alternates file");
 +
 +      if (lock) {
 +              fprintf_or_die(out, "%s\n", reference);
 +              if (commit_lock_file(lock))
 +                      die_errno("unable to move new alternates file into place");
 +              if (alt_odb_tail)
 +                      link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
 +      }
 +      free(alts);
  }
  
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
@@@ -454,7 -440,6 +454,7 @@@ void prepare_alt_odb(void
        read_info_alternates(get_object_directory(), 0);
  }
  
 +/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
  static int freshen_file(const char *fn)
  {
        struct utimbuf t;
        return !utime(fn, &t);
  }
  
 +/*
 + * All of the check_and_freshen functions return 1 if the file exists and was
 + * freshened (if freshening was requested), 0 otherwise. If they return
 + * 0, you should not assume that it is safe to skip a write of the object (it
 + * either does not exist on disk, or has a stale mtime and may be subject to
 + * pruning).
 + */
  static int check_and_freshen_file(const char *fn, int freshen)
  {
        if (access(fn, F_OK))
                return 0;
 -      if (freshen && freshen_file(fn))
 +      if (freshen && !freshen_file(fn))
                return 0;
        return 1;
  }
@@@ -652,15 -630,13 +652,15 @@@ static int check_packed_git_idx(const c
  int open_pack_index(struct packed_git *p)
  {
        char *idx_name;
 +      size_t len;
        int ret;
  
        if (p->index_data)
                return 0;
  
 -      idx_name = xstrdup(p->pack_name);
 -      strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
 +      if (!strip_suffix(p->pack_name, ".pack", &len))
 +              die("BUG: pack_name does not end in .pack");
 +      idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
        ret = check_packed_git_idx(idx_name, p);
        free(idx_name);
        return ret;
@@@ -728,8 -704,8 +728,8 @@@ static void mmap_limit_check(size_t len
                    (uintmax_t)length, (uintmax_t)limit);
  }
  
 -void *xmmap(void *start, size_t length,
 -      int prot, int flags, int fd, off_t offset)
 +void *xmmap_gently(void *start, size_t length,
 +                int prot, int flags, int fd, off_t offset)
  {
        void *ret;
  
                        return NULL;
                release_pack_memory(length);
                ret = mmap(start, length, prot, flags, fd, offset);
 -              if (ret == MAP_FAILED)
 -                      die_errno("Out of memory? mmap failed");
        }
        return ret;
  }
  
 +void *xmmap(void *start, size_t length,
 +      int prot, int flags, int fd, off_t offset)
 +{
 +      void *ret = xmmap_gently(start, length, prot, flags, fd, offset);
 +      if (ret == MAP_FAILED)
 +              die_errno("mmap failed");
 +      return ret;
 +}
 +
  void close_pack_windows(struct packed_git *p)
  {
        while (p->windows) {
        }
  }
  
 +static int close_pack_fd(struct packed_git *p)
 +{
 +      if (p->pack_fd < 0)
 +              return 0;
 +
 +      close(p->pack_fd);
 +      pack_open_fds--;
 +      p->pack_fd = -1;
 +
 +      return 1;
 +}
 +
 +static void close_pack(struct packed_git *p)
 +{
 +      close_pack_windows(p);
 +      close_pack_fd(p);
 +      close_pack_index(p);
 +}
 +
 +void close_all_packs(void)
 +{
 +      struct packed_git *p;
 +
 +      for (p = packed_git; p; p = p->next)
 +              if (p->do_not_close)
 +                      die("BUG! Want to close pack marked 'do-not-close'");
 +              else
 +                      close_pack(p);
 +}
 +
 +
  /*
   * The LRU pack is the one with the oldest MRU window, preferring packs
   * with no used windows, or the oldest mtime if it has no windows allocated.
@@@ -867,8 -805,12 +867,8 @@@ static int close_one_pack(void
                find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
        }
  
 -      if (lru_p) {
 -              close(lru_p->pack_fd);
 -              pack_open_fds--;
 -              lru_p->pack_fd = -1;
 -              return 1;
 -      }
 +      if (lru_p)
 +              return close_pack_fd(lru_p);
  
        return 0;
  }
@@@ -908,7 -850,12 +908,7 @@@ void free_pack_by_name(const char *pack
                p = *pp;
                if (strcmp(pack_name, p->pack_name) == 0) {
                        clear_delta_base_cache();
 -                      close_pack_windows(p);
 -                      if (p->pack_fd != -1) {
 -                              close(p->pack_fd);
 -                              pack_open_fds--;
 -                      }
 -                      close_pack_index(p);
 +                      close_pack(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        if (last_found_pack == p)
@@@ -1042,7 -989,11 +1042,7 @@@ static int open_packed_git(struct packe
  {
        if (!open_packed_git_1(p))
                return 0;
 -      if (p->pack_fd != -1) {
 -              close(p->pack_fd);
 -              pack_open_fds--;
 -              p->pack_fd = -1;
 -      }
 +      close_pack_fd(p);
        return -1;
  }
  
@@@ -1108,8 -1059,11 +1108,8 @@@ unsigned char *use_pack(struct packed_g
                                        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;
 -                      }
 +                              && !p->do_not_close)
 +                              close_pack_fd(p);
                        pack_mmap_calls++;
                        pack_open_windows++;
                        if (pack_mapped > peak_pack_mapped)
@@@ -1144,12 -1098,11 +1144,12 @@@ static void try_to_free_pack_memory(siz
        release_pack_memory(size);
  }
  
 -struct packed_git *add_packed_git(const char *path, int path_len, int local)
 +struct packed_git *add_packed_git(const char *path, size_t 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);
 +      size_t alloc;
 +      struct packed_git *p;
  
        if (!have_set_try_to_free_routine) {
                have_set_try_to_free_routine = 1;
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
         */
 -      path_len -= strlen(".idx");
 -      if (path_len < 1) {
 -              free(p);
 +      if (!strip_suffix_mem(path, &path_len, ".idx"))
                return NULL;
 -      }
 +
 +      /*
 +       * ".pack" is long enough to hold any suffix we're adding (and
 +       * the use xsnprintf double-checks that)
 +       */
 +      alloc = path_len + strlen(".pack") + 1;
 +      p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
  
 -      strcpy(p->pack_name + path_len, ".keep");
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");
        if (!access(p->pack_name, F_OK))
                p->pack_keep = 1;
  
 -      strcpy(p->pack_name + path_len, ".pack");
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
                return NULL;
  struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
  {
        const char *path = sha1_pack_name(sha1);
 -      struct packed_git *p = alloc_packed_git(strlen(path) + 1);
 +      int alloc = strlen(path) + 1;
 +      struct packed_git *p = alloc_packed_git(alloc);
  
 -      strcpy(p->pack_name, path);
 +      memcpy(p->pack_name, path, alloc); /* includes NUL */
        hashcpy(p->sha1, sha1);
        if (check_packed_git_idx(idx_path, p)) {
                free(p);
@@@ -1247,7 -1195,7 +1247,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;
@@@ -1468,7 -1416,7 +1468,7 @@@ int check_sha1_signature(const unsigne
                return -1;
  
        /* Generate the header */
 -      hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj_type), size) + 1;
  
        /* Sha1.. */
        git_SHA1_Init(&c);
@@@ -1495,10 -1443,7 +1495,10 @@@ int git_open_noatime(const char *name
        static int sha1_file_open_flag = O_NOATIME;
  
        for (;;) {
 -              int fd = open(name, O_RDONLY | sha1_file_open_flag);
 +              int fd;
 +
 +              errno = 0;
 +              fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
                        return fd;
  
@@@ -1616,40 -1561,6 +1616,40 @@@ int unpack_sha1_header(git_zstream *str
        return git_inflate(stream, 0);
  }
  
 +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
 +                                      unsigned long mapsize, void *buffer,
 +                                      unsigned long bufsiz, struct strbuf *header)
 +{
 +      int status;
 +
 +      status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
 +
 +      /*
 +       * Check if entire header is unpacked in the first iteration.
 +       */
 +      if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +              return 0;
 +
 +      /*
 +       * buffer[0..bufsiz] was not large enough.  Copy the partial
 +       * result out to header, and then append the result of further
 +       * reading the stream.
 +       */
 +      strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +      stream->next_out = buffer;
 +      stream->avail_out = bufsiz;
 +
 +      do {
 +              status = git_inflate(stream, 0);
 +              strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +              if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +                      return 0;
 +              stream->next_out = buffer;
 +              stream->avail_out = bufsiz;
 +      } while (status != Z_STREAM_END);
 +      return -1;
 +}
 +
  static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
  {
        int bytes = strlen(buffer) + 1;
   * too permissive for what we want to check. So do an anal
   * object header parse by hand.
   */
 -int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
 +                             unsigned int flags)
  {
 -      char type[10];
 -      int i;
 +      const char *type_buf = hdr;
        unsigned long size;
 +      int type, type_len = 0;
  
        /*
 -       * The type can be at most ten bytes (including the
 -       * terminating '\0' that we add), and is followed by
 +       * The type can be of any size but is followed by
         * a space.
         */
 -      i = 0;
        for (;;) {
                char c = *hdr++;
                if (c == ' ')
                        break;
 -              type[i++] = c;
 -              if (i >= sizeof(type))
 -                      return -1;
 +              type_len++;
        }
 -      type[i] = 0;
 +
 +      type = type_from_string_gently(type_buf, type_len, 1);
 +      if (oi->typename)
 +              strbuf_add(oi->typename, type_buf, type_len);
 +      /*
 +       * Set type to 0 if its an unknown object and
 +       * we're obtaining the type using '--allow-unkown-type'
 +       * option.
 +       */
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
 +              type = 0;
 +      else if (type < 0)
 +              die("invalid object type");
 +      if (oi->typep)
 +              *oi->typep = type;
  
        /*
         * The length must follow immediately, and be in canonical
                        size = size * 10 + c;
                }
        }
 -      *sizep = size;
 +
 +      if (oi->sizep)
 +              *oi->sizep = size;
  
        /*
         * The length must be followed by a zero byte
         */
 -      return *hdr ? -1 : type_from_string(type);
 +      return *hdr ? -1 : type;
 +}
 +
 +int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +{
 +      struct object_info oi;
 +
 +      oi.sizep = sizep;
 +      oi.typename = NULL;
 +      oi.typep = NULL;
 +      return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
  }
  
  static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@@ -2126,7 -2014,7 +2126,7 @@@ static unsigned long pack_entry_hash(st
  {
        unsigned long hash;
  
-       hash = (unsigned long)p + (unsigned long)base_offset;
+       hash = (unsigned long)(intptr_t)p + (unsigned long)base_offset;
        hash += (hash >> 8) + (hash >> 16);
        return hash % MAX_DELTA_CACHE;
  }
@@@ -2582,8 -2470,10 +2582,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);
@@@ -2631,15 -2521,13 +2631,15 @@@ struct packed_git *find_sha1_pack(cons
  }
  
  static int sha1_loose_object_info(const unsigned char *sha1,
 -                                struct object_info *oi)
 +                                struct object_info *oi,
 +                                int flags)
  {
 -      int status;
 -      unsigned long mapsize, size;
 +      int status = 0;
 +      unsigned long mapsize;
        void *map;
        git_zstream stream;
        char hdr[32];
 +      struct strbuf hdrbuf = STRBUF_INIT;
  
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
         * return value implicitly indicates whether the
         * object even exists.
         */
 -      if (!oi->typep && !oi->sizep) {
 +      if (!oi->typep && !oi->typename && !oi->sizep) {
                struct stat st;
                if (stat_sha1_file(sha1, &st) < 0)
                        return -1;
                return -1;
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
 -      if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
 +              if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
 +                      status = error("unable to unpack %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
 -      else if ((status = parse_sha1_header(hdr, &size)) < 0)
 +      if (status < 0)
 +              ; /* Do nothing */
 +      else if (hdrbuf.len) {
 +              if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
 +                      status = error("unable to parse %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
 -      else if (oi->sizep)
 -              *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
 -      if (oi->typep)
 +      if (status && oi->typep)
                *oi->typep = status;
 +      strbuf_release(&hdrbuf);
        return 0;
  }
  
@@@ -2694,7 -2573,6 +2694,7 @@@ int sha1_object_info_extended(const uns
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
 +      enum object_type real_type;
        const unsigned char *real = lookup_replace_object_extended(sha1, flags);
  
        co = find_cached_object(real);
                        *(oi->disk_sizep) = 0;
                if (oi->delta_base_sha1)
                        hashclr(oi->delta_base_sha1);
 +              if (oi->typename)
 +                      strbuf_addstr(oi->typename, typename(co->type));
                oi->whence = OI_CACHED;
                return 0;
        }
  
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
 -              if (!sha1_loose_object_info(real, oi)) {
 +              if (!sha1_loose_object_info(real, oi, flags)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
                        return -1;
        }
  
 +      /*
 +       * packed_object_info() does not follow the delta chain to
 +       * find out the real type, unless it is given oi->typep.
 +       */
 +      if (oi->typename && !oi->typep)
 +              oi->typep = &real_type;
 +
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
 +              if (oi->typep == &real_type)
 +                      oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
 +      if (oi->typename)
 +              strbuf_addstr(oi->typename, typename(*oi->typep));
 +      if (oi->typep == &real_type)
 +              oi->typep = NULL;
  
        return 0;
  }
@@@ -2934,7 -2797,7 +2934,7 @@@ static void write_sha1_file_prepare(con
        git_SHA_CTX c;
  
        /* Generate the header */
 -      *hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
 +      *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1;
  
        /* Sha1.. */
        git_SHA1_Init(&c);
  
  /*
   * Move the just written object into its final resting place.
 - * NEEDSWORK: this should be renamed to finalize_temp_file() as
 - * "moving" is only a part of what it does, when no patch between
 - * master to pu changes the call sites of this function.
   */
 -int move_temp_to_file(const char *tmpfile, const char *filename)
 +int finalize_object_file(const char *tmpfile, const char *filename)
  {
        int ret = 0;
  
@@@ -2997,7 -2863,7 +2997,7 @@@ int hash_sha1_file(const void *buf, uns
                     unsigned char *sha1)
  {
        char hdr[32];
 -      int hdrlen;
 +      int hdrlen = sizeof(hdr);
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        return 0;
  }
@@@ -3027,31 -2893,29 +3027,31 @@@ static inline int directory_size(const 
   * We want to avoid cross-directory filename renames, because those
   * can have problems on various filesystems (FAT, NFS, Coda).
   */
 -static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
 +static int create_tmpfile(struct strbuf *tmp, const char *filename)
  {
        int fd, dirlen = directory_size(filename);
  
 -      if (dirlen + 20 > bufsiz) {
 -              errno = ENAMETOOLONG;
 -              return -1;
 -      }
 -      memcpy(buffer, filename, dirlen);
 -      strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
 -      fd = git_mkstemp_mode(buffer, 0444);
 +      strbuf_reset(tmp);
 +      strbuf_add(tmp, filename, dirlen);
 +      strbuf_addstr(tmp, "tmp_obj_XXXXXX");
 +      fd = git_mkstemp_mode(tmp->buf, 0444);
        if (fd < 0 && dirlen && errno == ENOENT) {
 -              /* Make sure the directory exists */
 -              memcpy(buffer, filename, dirlen);
 -              buffer[dirlen-1] = 0;
 -              if (mkdir(buffer, 0777) && errno != EEXIST)
 +              /*
 +               * Make sure the directory exists; note that the contents
 +               * of the buffer are undefined after mkstemp returns an
 +               * error, so we have to rewrite the whole buffer from
 +               * scratch.
 +               */
 +              strbuf_reset(tmp);
 +              strbuf_add(tmp, filename, dirlen - 1);
 +              if (mkdir(tmp->buf, 0777) && errno != EEXIST)
                        return -1;
 -              if (adjust_shared_perm(buffer))
 +              if (adjust_shared_perm(tmp->buf))
                        return -1;
  
                /* Try again */
 -              strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
 -              fd = git_mkstemp_mode(buffer, 0444);
 +              strbuf_addstr(tmp, "/tmp_obj_XXXXXX");
 +              fd = git_mkstemp_mode(tmp->buf, 0444);
        }
        return fd;
  }
@@@ -3064,10 -2928,10 +3064,10 @@@ static int write_loose_object(const uns
        git_zstream stream;
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
 -      static char tmp_file[PATH_MAX];
 +      static struct strbuf tmp_file = STRBUF_INIT;
        const char *filename = sha1_file_name(sha1);
  
 -      fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
 +      fd = create_tmpfile(&tmp_file, filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
        }
  
        /* Set it up */
 -      memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        stream.next_out = compressed;
        stream.avail_out = sizeof(compressed);
                struct utimbuf utb;
                utb.actime = mtime;
                utb.modtime = mtime;
 -              if (utime(tmp_file, &utb) < 0)
 +              if (utime(tmp_file.buf, &utb) < 0)
                        warning("failed utime() on %s: %s",
 -                              tmp_file, strerror(errno));
 +                              tmp_file.buf, strerror(errno));
        }
  
 -      return move_temp_to_file(tmp_file, filename);
 +      return finalize_object_file(tmp_file.buf, filename);
  }
  
  static int freshen_loose_object(const unsigned char *sha1)
  static int freshen_packed_object(const unsigned char *sha1)
  {
        struct pack_entry e;
 -      return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
 +      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 *returnsha1)
 +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;
 +      int hdrlen = sizeof(hdr);
  
        /* Normally if we have it in the pack then we do not bother writing
         * it out into .git/objects/??/?{38} file.
         */
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
 -      if (returnsha1)
 -              hashcpy(returnsha1, sha1);
 -      if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
 +      if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
  }
  
 +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 */
 +      hdrlen = strlen(type) + 32;
 +      header = xmalloc(hdrlen);
 +      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;
        buf = read_packed_sha1(sha1, &type, &len);
        if (!buf)
                return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
 -      hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
        ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
        free(buf);
  
@@@ -3213,7 -3052,7 +3213,7 @@@ int has_sha1_pack(const unsigned char *
        return find_pack_entry(sha1, &e);
  }
  
 -int has_sha1_file(const unsigned char *sha1)
 +int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
  {
        struct pack_entry e;
  
                return 1;
        if (has_loose_object(sha1))
                return 1;
 +      if (flags & HAS_SHA1_QUICK)
 +              return 0;
        reprepare_packed_git();
        return find_pack_entry(sha1, &e);
  }
@@@ -3340,7 -3177,7 +3340,7 @@@ static int index_core(unsigned char *sh
        int ret;
  
        if (!size) {
 -              ret = index_mem(sha1, NULL, size, type, path, flags);
 +              ret = index_mem(sha1, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
@@@ -3519,42 -3356,31 +3519,42 @@@ static int for_each_file_in_obj_subdir(
        return r;
  }
  
 -int for_each_loose_file_in_objdir(const char *path,
 +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)
  {
 -      struct strbuf buf = STRBUF_INIT;
 -      size_t baselen;
 +      size_t baselen = path->len;
        int r = 0;
        int i;
  
 -      strbuf_addstr(&buf, path);
 -      strbuf_addch(&buf, '/');
 -      baselen = buf.len;
 -
        for (i = 0; i < 256; i++) {
 -              strbuf_addf(&buf, "%02x", i);
 -              r = for_each_file_in_obj_subdir(i, &buf, obj_cb, cruft_cb,
 +              strbuf_addf(path, "/%02x", i);
 +              r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb,
                                                subdir_cb, data);
 -              strbuf_setlen(&buf, baselen);
 +              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;
  }
  
@@@ -3567,19 -3393,12 +3567,19 @@@ static int loose_from_alt_odb(struct al
                              void *vdata)
  {
        struct loose_alt_odb_data *data = vdata;
 -      return for_each_loose_file_in_objdir(alt->base,
 -                                           data->cb, NULL, NULL,
 -                                           data->data);
 +      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)
 +int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags)
  {
        struct loose_alt_odb_data alt;
        int r;
        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);
@@@ -3616,23 -3432,16 +3616,23 @@@ static int for_each_object_in_pack(stru
        return r;
  }
  
 -int for_each_packed_object(each_packed_object_fn cb, void *data)
 +int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
  {
        struct packed_git *p;
        int r = 0;
 +      int pack_errors = 0;
  
        prepare_packed_git();
        for (p = packed_git; p; p = p->next) {
 +              if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
 +                      continue;
 +              if (open_pack_index(p)) {
 +                      pack_errors = 1;
 +                      continue;
 +              }
                r = for_each_object_in_pack(p, cb, data);
                if (r)
                        break;
        }
 -      return r;
 +      return r ? r : pack_errors;
  }