Merge branch 'rs/deflate-init-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Mar 2015 23:01:26 +0000 (16:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Mar 2015 23:01:26 +0000 (16:01 -0700)
Code simplification.

* rs/deflate-init-cleanup:
zlib: initialize git_zstream in git_deflate_init{,_gzip,_raw}

1  2 
builtin/index-pack.c
builtin/pack-objects.c
bulk-checkin.c
diff.c
fast-import.c
http-push.c
remote-curl.c
sha1_file.c
diff --combined builtin/index-pack.c
index 46321176719808dd53a38675dd0d9c558ae336dd,1239a10d0e197ee88faafc5f0e8982aeb6f64a5b..cf654df09b3734063f415b2b735f3062706f75a0
@@@ -112,10 -112,6 +112,10 @@@ static pthread_mutex_t deepest_delta_mu
  #define deepest_delta_lock()  lock_mutex(&deepest_delta_mutex)
  #define deepest_delta_unlock()        unlock_mutex(&deepest_delta_mutex)
  
 +static pthread_mutex_t type_cas_mutex;
 +#define type_cas_lock()               lock_mutex(&type_cas_mutex)
 +#define type_cas_unlock()     unlock_mutex(&type_cas_mutex)
 +
  static pthread_key_t key;
  
  static inline void lock_mutex(pthread_mutex_t *mutex)
@@@ -139,7 -135,6 +139,7 @@@ static void init_thread(void
        init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&counter_mutex, NULL);
        pthread_mutex_init(&work_mutex, NULL);
 +      pthread_mutex_init(&type_cas_mutex, NULL);
        if (show_stat)
                pthread_mutex_init(&deepest_delta_mutex, NULL);
        pthread_key_create(&key, NULL);
@@@ -162,7 -157,6 +162,7 @@@ static void cleanup_thread(void
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&counter_mutex);
        pthread_mutex_destroy(&work_mutex);
 +      pthread_mutex_destroy(&type_cas_mutex);
        if (show_stat)
                pthread_mutex_destroy(&deepest_delta_mutex);
        for (i = 0; i < nr_threads; i++)
  #define deepest_delta_lock()
  #define deepest_delta_unlock()
  
 +#define type_cas_lock()
 +#define type_cas_unlock()
 +
  #endif
  
  
@@@ -447,7 -438,7 +447,7 @@@ static void *unpack_entry_data(unsigne
        if (type == OBJ_BLOB && size > big_file_threshold)
                buf = fixed_buf;
        else
 -              buf = xmalloc(size);
 +              buf = xmallocz(size);
  
        memset(&stream, 0, sizeof(stream));
        git_inflate_init(&stream);
@@@ -552,7 -543,7 +552,7 @@@ static void *unpack_data(struct object_
        git_zstream stream;
        int status;
  
 -      data = xmalloc(consume ? 64*1024 : obj->size);
 +      data = xmallocz(consume ? 64*1024 : obj->size);
        inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
  
        memset(&stream, 0, sizeof(stream));
@@@ -782,8 -773,7 +782,8 @@@ static void sha1_object(const void *dat
                        if (!obj)
                                die(_("invalid %s"), typename(type));
                        if (do_fsck_object &&
 -                          fsck_object(obj, 1, fsck_error_function))
 +                          fsck_object(obj, buf, size, 1,
 +                                  fsck_error_function))
                                die(_("Error in object"));
                        if (fsck_walk(obj, mark_link, NULL))
                                die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
@@@ -872,6 -862,7 +872,6 @@@ static void resolve_delta(struct object
  {
        void *base_data, *delta_data;
  
 -      delta_obj->real_type = base->obj->real_type;
        if (show_stat) {
                delta_obj->delta_depth = base->obj->delta_depth + 1;
                deepest_delta_lock();
        counter_unlock();
  }
  
 +/*
 + * Standard boolean compare-and-swap: atomically check whether "*type" is
 + * "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
 + * and return false.
 + */
 +static int compare_and_swap_type(enum object_type *type,
 +                               enum object_type want,
 +                               enum object_type set)
 +{
 +      enum object_type old;
 +
 +      type_cas_lock();
 +      old = *type;
 +      if (old == want)
 +              *type = set;
 +      type_cas_unlock();
 +
 +      return old == want;
 +}
 +
  static struct base_data *find_unresolved_deltas_1(struct base_data *base,
                                                  struct base_data *prev_base)
  {
                struct object_entry *child = objects + deltas[base->ref_first].obj_no;
                struct base_data *result = alloc_base_data();
  
 -              assert(child->real_type == OBJ_REF_DELTA);
 +              if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
 +                                         base->obj->real_type))
 +                      die("BUG: child->real_type != OBJ_REF_DELTA");
 +
                resolve_delta(child, base, result);
                if (base->ref_first == base->ref_last && base->ofs_last == -1)
                        free_base_data(base);
                struct base_data *result = alloc_base_data();
  
                assert(child->real_type == OBJ_OFS_DELTA);
 +              child->real_type = base->obj->real_type;
                resolve_delta(child, base, result);
                if (base->ofs_first == base->ofs_last)
                        free_base_data(base);
@@@ -1173,7 -1140,9 +1173,7 @@@ static void conclude_pack(int fix_thin_
                int nr_objects_initial = nr_objects;
                if (nr_unresolved <= 0)
                        die(_("confusion beyond insanity"));
 -              objects = xrealloc(objects,
 -                                 (nr_objects + nr_unresolved + 1)
 -                                 * sizeof(*objects));
 +              REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
                memset(objects + nr_objects + 1, 0,
                       nr_unresolved * sizeof(*objects));
                f = sha1fd(output_fd, curr_pack);
@@@ -1204,7 -1173,6 +1204,6 @@@ static int write_compressed(struct sha1
        int status;
        unsigned char outbuf[4096];
  
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        stream.next_in = in;
        stream.avail_in = size;
@@@ -1536,8 -1504,7 +1535,8 @@@ int cmd_index_pack(int argc, const cha
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
 -      char *index_name_buf = NULL, *keep_name_buf = NULL;
 +      struct strbuf index_name_buf = STRBUF_INIT,
 +                    keep_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
        if (!index_name && pack_name) {
 -              int len = strlen(pack_name);
 -              if (!has_extension(pack_name, ".pack"))
 +              size_t len;
 +              if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
 -              index_name_buf = xmalloc(len);
 -              memcpy(index_name_buf, pack_name, len - 5);
 -              strcpy(index_name_buf + len - 5, ".idx");
 -              index_name = index_name_buf;
 +              strbuf_add(&index_name_buf, pack_name, len);
 +              strbuf_addstr(&index_name_buf, ".idx");
 +              index_name = index_name_buf.buf;
        }
        if (keep_msg && !keep_name && pack_name) {
 -              int len = strlen(pack_name);
 -              if (!has_extension(pack_name, ".pack"))
 +              size_t len;
 +              if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
 -              keep_name_buf = xmalloc(len);
 -              memcpy(keep_name_buf, pack_name, len - 5);
 -              strcpy(keep_name_buf + len - 5, ".keep");
 -              keep_name = keep_name_buf;
 +              strbuf_add(&keep_name_buf, pack_name, len);
 +              strbuf_addstr(&keep_name_buf, ".idx");
 +              keep_name = keep_name_buf.buf;
        }
        if (verify) {
                if (!index_name)
        else
                close(input_fd);
        free(objects);
 -      free(index_name_buf);
 -      free(keep_name_buf);
 +      strbuf_release(&index_name_buf);
 +      strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
diff --combined builtin/pack-objects.c
index d8165878e1d466e49719038b1badc524d5627270,1d1ed1b725ee4ece5129167f0a9a3a1315f00088..c3a75166bd10918d21d3df19832880c4373dc8d7
@@@ -20,9 -20,6 +20,9 @@@
  #include "streaming.h"
  #include "thread-utils.h"
  #include "pack-bitmap.h"
 +#include "reachable.h"
 +#include "sha1-array.h"
 +#include "argv-array.h"
  
  static const char *pack_usage[] = {
        N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@@ -92,7 -89,8 +92,7 @@@ static void index_commit_for_bitmap(str
  {
        if (indexed_commits_nr >= indexed_commits_alloc) {
                indexed_commits_alloc = (indexed_commits_alloc + 32) * 2;
 -              indexed_commits = xrealloc(indexed_commits,
 -                      indexed_commits_alloc * sizeof(struct commit *));
 +              REALLOC_ARRAY(indexed_commits, indexed_commits_alloc);
        }
  
        indexed_commits[indexed_commits_nr++] = commit;
@@@ -125,7 -123,6 +125,6 @@@ static unsigned long do_compress(void *
        void *in, *out;
        unsigned long maxsize;
  
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, pack_compression_level);
        maxsize = git_deflate_bound(&stream, size);
  
@@@ -153,7 -150,6 +152,6 @@@ static unsigned long write_large_blob_d
        unsigned char obuf[1024 * 16];
        unsigned long olen = 0;
  
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, pack_compression_level);
  
        for (;;) {
@@@ -814,7 -810,6 +812,7 @@@ static void write_pack_file(void
                        fixup_pack_header_footer(fd, sha1, pack_tmp_name,
                                                 nr_written, sha1, offset);
                        close(fd);
 +                      write_bitmap_index = 0;
                }
  
                if (!pack_to_stdout) {
@@@ -1976,6 -1971,8 +1974,6 @@@ static void ll_find_deltas(struct objec
  
        init_threaded_search();
  
 -      if (!delta_search_threads)      /* --threads=0 means autodetect */
 -              delta_search_threads = online_cpus();
        if (delta_search_threads <= 1) {
                find_deltas(list, &list_size, window, depth, processed);
                cleanup_threaded_search();
@@@ -2215,6 -2212,10 +2213,6 @@@ static int git_pack_config(const char *
                cache_max_small_delta_size = git_config_int(k, v);
                return 0;
        }
 -      if (!strcmp(k, "pack.writebitmaps")) {
 -              write_bitmap_index = git_config_bool(k, v);
 -              return 0;
 -      }
        if (!strcmp(k, "pack.writebitmaphashcache")) {
                if (git_config_bool(k, v))
                        write_bitmap_options |= BITMAP_OPT_HASH_CACHE;
@@@ -2409,27 -2410,6 +2407,27 @@@ static int has_sha1_pack_kept_or_nonloc
        return 0;
  }
  
 +/*
 + * Store a list of sha1s that are should not be discarded
 + * because they are either written too recently, or are
 + * reachable from another object that was.
 + *
 + * This is filled by get_object_list.
 + */
 +static struct sha1_array recent_objects;
 +
 +static int loosened_object_can_be_discarded(const unsigned char *sha1,
 +                                          unsigned long mtime)
 +{
 +      if (!unpack_unreachable_expiration)
 +              return 0;
 +      if (mtime > unpack_unreachable_expiration)
 +              return 0;
 +      if (sha1_array_lookup(&recent_objects, sha1) >= 0)
 +              return 0;
 +      return 1;
 +}
 +
  static void loosen_unused_packed_objects(struct rev_info *revs)
  {
        struct packed_git *p;
                if (!p->pack_local || p->pack_keep)
                        continue;
  
 -              if (unpack_unreachable_expiration &&
 -                  p->mtime < unpack_unreachable_expiration)
 -                      continue;
 -
                if (open_pack_index(p))
                        die("cannot open pack index");
  
                for (i = 0; i < p->num_objects; i++) {
                        sha1 = nth_packed_object_sha1(p, i);
                        if (!packlist_find(&to_pack, sha1, NULL) &&
 -                              !has_sha1_pack_kept_or_nonlocal(sha1))
 +                          !has_sha1_pack_kept_or_nonlocal(sha1) &&
 +                          !loosened_object_can_be_discarded(sha1, p->mtime))
                                if (force_object_loose(sha1, p->mtime))
                                        die("unable to force loose object");
                }
@@@ -2483,19 -2466,6 +2481,19 @@@ static int get_object_list_from_bitmap(
        return 0;
  }
  
 +static void record_recent_object(struct object *obj,
 +                               const struct name_path *path,
 +                               const char *last,
 +                               void *data)
 +{
 +      sha1_array_append(&recent_objects, obj->sha1);
 +}
 +
 +static void record_recent_commit(struct commit *commit, void *data)
 +{
 +      sha1_array_append(&recent_objects, commit->object.sha1);
 +}
 +
  static void get_object_list(int ac, const char **av)
  {
        struct rev_info revs;
                                if (get_sha1_hex(line + 10, sha1))
                                        die("not an SHA-1 '%s'", line + 10);
                                register_shallow(sha1);
 +                              use_bitmap_index = 0;
                                continue;
                        }
                        die("not a rev '%s'", line);
        mark_edges_uninteresting(&revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object, NULL);
  
 +      if (unpack_unreachable_expiration) {
 +              revs.ignore_missing_links = 1;
 +              if (add_unseen_recent_objects_to_traversal(&revs,
 +                              unpack_unreachable_expiration))
 +                      die("unable to add recent objects");
 +              if (prepare_revision_walk(&revs))
 +                      die("revision walk setup failed");
 +              traverse_commit_list(&revs, record_recent_commit,
 +                                   record_recent_object, NULL);
 +      }
 +
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
 +
 +      sha1_array_clear(&recent_objects);
  }
  
  static int option_parse_index_version(const struct option *opt,
@@@ -2613,11 -2569,10 +2611,11 @@@ int cmd_pack_objects(int argc, const ch
  {
        int use_internal_rev_list = 0;
        int thin = 0;
 +      int shallow = 0;
        int all_progress_implied = 0;
 -      const char *rp_av[6];
 -      int rp_ac = 0;
 +      struct argv_array rp = ARGV_ARRAY_INIT;
        int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
 +      int rev_list_index = 0;
        struct option pack_objects_options[] = {
                OPT_SET_INT('q', "quiet", &progress,
                            N_("do not show progress meter"), 0),
                { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
                  N_("include objects referred by reflog entries"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
 +              { OPTION_SET_INT, 0, "indexed-objects", &rev_list_index, NULL,
 +                N_("include objects referred to by the index"),
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
                OPT_BOOL(0, "stdout", &pack_to_stdout,
                         N_("output pack to stdout")),
                OPT_BOOL(0, "include-tag", &include_tag,
                  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
                OPT_BOOL(0, "thin", &thin,
                         N_("create thin packs")),
 +              OPT_BOOL(0, "shallow", &shallow,
 +                       N_("create packs suitable for shallow fetches")),
                OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
                         N_("ignore packs that have companion .keep file")),
                OPT_INTEGER(0, "compression", &pack_compression_level,
        if (pack_to_stdout != !base_name || argc)
                usage_with_options(pack_usage, pack_objects_options);
  
 -      rp_av[rp_ac++] = "pack-objects";
 +      argv_array_push(&rp, "pack-objects");
        if (thin) {
                use_internal_rev_list = 1;
 -              rp_av[rp_ac++] = "--objects-edge";
 +              argv_array_push(&rp, shallow
 +                              ? "--objects-edge-aggressive"
 +                              : "--objects-edge");
        } else
 -              rp_av[rp_ac++] = "--objects";
 +              argv_array_push(&rp, "--objects");
  
        if (rev_list_all) {
                use_internal_rev_list = 1;
 -              rp_av[rp_ac++] = "--all";
 +              argv_array_push(&rp, "--all");
        }
        if (rev_list_reflog) {
                use_internal_rev_list = 1;
 -              rp_av[rp_ac++] = "--reflog";
 +              argv_array_push(&rp, "--reflog");
 +      }
 +      if (rev_list_index) {
 +              use_internal_rev_list = 1;
 +              argv_array_push(&rp, "--indexed-objects");
        }
        if (rev_list_unpacked) {
                use_internal_rev_list = 1;
 -              rp_av[rp_ac++] = "--unpacked";
 +              argv_array_push(&rp, "--unpacked");
        }
  
        if (!reuse_object)
                pack_compression_level = Z_DEFAULT_COMPRESSION;
        else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
                die("bad pack compression level %d", pack_compression_level);
 +
 +      if (!delta_search_threads)      /* --threads=0 means autodetect */
 +              delta_search_threads = online_cpus();
 +
  #ifdef NO_PTHREADS
        if (delta_search_threads != 1)
                warning("no threads support, ignoring --threads");
  
        if (keep_unreachable && unpack_unreachable)
                die("--keep-unreachable and --unpack-unreachable are incompatible.");
 +      if (!rev_list_all || !rev_list_reflog || !rev_list_index)
 +              unpack_unreachable_expiration = 0;
  
        if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
                use_bitmap_index = 0;
        if (!use_internal_rev_list)
                read_object_list_from_stdin();
        else {
 -              rp_av[rp_ac] = NULL;
 -              get_object_list(rp_ac, rp_av);
 +              get_object_list(rp.argc, rp.argv);
 +              argv_array_clear(&rp);
        }
        cleanup_preferred_base();
        if (include_tag && nr_result)
diff --combined bulk-checkin.c
index 0c4b8a7cad085fa1c7760e6290f21213830eda3d,136b704627d38b45c889568d009cd10d96bb3747..8d157eba455f15a00cc7bf9b970f1862167ee1bb
@@@ -1,7 -1,6 +1,7 @@@
  /*
   * Copyright (c) 2011, Google Inc.
   */
 +#include "cache.h"
  #include "bulk-checkin.h"
  #include "csum-file.h"
  #include "pack.h"
@@@ -105,7 -104,6 +105,6 @@@ static int stream_to_pack(struct bulk_c
        int write_object = (flags & HASH_WRITE_OBJECT);
        off_t offset = 0;
  
-       memset(&s, 0, sizeof(s));
        git_deflate_init(&s, pack_compression_level);
  
        hdrlen = encode_in_pack_object_header(type, size, obuf);
diff --combined diff.c
index abc32c8a7dcaf911c4b9af5168a7da35a5d5e24b,c90cf72e0af68d9942e92d3d635fddfb975f05b8..7500c5509550ccd9a86d620e06c51a71d61f8b6c
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -52,23 -52,23 +52,23 @@@ static char diff_colors[][COLOR_MAXLEN
        GIT_COLOR_NORMAL,       /* FUNCINFO */
  };
  
 -static int parse_diff_color_slot(const char *var, int ofs)
 +static int parse_diff_color_slot(const char *var)
  {
 -      if (!strcasecmp(var+ofs, "plain"))
 +      if (!strcasecmp(var, "plain"))
                return DIFF_PLAIN;
 -      if (!strcasecmp(var+ofs, "meta"))
 +      if (!strcasecmp(var, "meta"))
                return DIFF_METAINFO;
 -      if (!strcasecmp(var+ofs, "frag"))
 +      if (!strcasecmp(var, "frag"))
                return DIFF_FRAGINFO;
 -      if (!strcasecmp(var+ofs, "old"))
 +      if (!strcasecmp(var, "old"))
                return DIFF_FILE_OLD;
 -      if (!strcasecmp(var+ofs, "new"))
 +      if (!strcasecmp(var, "new"))
                return DIFF_FILE_NEW;
 -      if (!strcasecmp(var+ofs, "commit"))
 +      if (!strcasecmp(var, "commit"))
                return DIFF_COMMIT;
 -      if (!strcasecmp(var+ofs, "whitespace"))
 +      if (!strcasecmp(var, "whitespace"))
                return DIFF_WHITESPACE;
 -      if (!strcasecmp(var+ofs, "func"))
 +      if (!strcasecmp(var, "func"))
                return DIFF_FUNCINFO;
        return -1;
  }
@@@ -231,8 -231,6 +231,8 @@@ int git_diff_ui_config(const char *var
  
  int git_diff_basic_config(const char *var, const char *value, void *cb)
  {
 +      const char *name;
 +
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
                return 0;
        if (userdiff_config(var, value) < 0)
                return -1;
  
 -      if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
 -              int slot = parse_diff_color_slot(var, 11);
 +      if (skip_prefix(var, "diff.color.", &name) ||
 +          skip_prefix(var, "color.diff.", &name)) {
 +              int slot = parse_diff_color_slot(name);
                if (slot < 0)
                        return 0;
                if (!value)
                        return config_error_nonbool(var);
 -              color_parse(value, var, diff_colors[slot]);
 -              return 0;
 +              return color_parse(value, diff_colors[slot]);
        }
  
        /* like GNU diff's --suppress-blank-empty option  */
@@@ -375,7 -373,7 +375,7 @@@ static unsigned long diff_filespec_size
  {
        if (!DIFF_FILE_VALID(one))
                return 0;
 -      diff_populate_filespec(one, 1);
 +      diff_populate_filespec(one, CHECK_SIZE_ONLY);
        return one->size;
  }
  
@@@ -524,9 -522,9 +524,9 @@@ static void emit_hunk_header(struct emi
        ep += 2; /* skip over @@ */
  
        /* The hunk header in fraginfo color */
 -      strbuf_add(&msgbuf, frag, strlen(frag));
 +      strbuf_addstr(&msgbuf, frag);
        strbuf_add(&msgbuf, line, ep - line);
 -      strbuf_add(&msgbuf, reset, strlen(reset));
 +      strbuf_addstr(&msgbuf, reset);
  
        /*
         * trailing "\r\n"
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
 -              strbuf_add(&msgbuf, plain, strlen(plain));
 +              strbuf_addstr(&msgbuf, plain);
                strbuf_add(&msgbuf, cp, ep - cp);
 -              strbuf_add(&msgbuf, reset, strlen(reset));
 +              strbuf_addstr(&msgbuf, reset);
        }
  
        if (ep < line + len) {
 -              strbuf_add(&msgbuf, func, strlen(func));
 +              strbuf_addstr(&msgbuf, func);
                strbuf_add(&msgbuf, ep, line + len - ep);
 -              strbuf_add(&msgbuf, reset, strlen(reset));
 +              strbuf_addstr(&msgbuf, reset);
        }
  
        strbuf_add(&msgbuf, line + len, org_len - len);
@@@ -1909,11 -1907,11 +1909,11 @@@ static void show_dirstat(struct diff_op
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
                } else if (DIFF_FILE_VALID(p->one)) {
 -                      diff_populate_filespec(p->one, 1);
 +                      diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
                        copied = added = 0;
                        diff_free_filespec_data(p->one);
                } else if (DIFF_FILE_VALID(p->two)) {
 -                      diff_populate_filespec(p->two, 1);
 +                      diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
                        copied = 0;
                        added = p->two->size;
                        diff_free_filespec_data(p->two);
@@@ -2093,7 -2091,6 +2093,6 @@@ static unsigned char *deflate_it(char *
        unsigned char *deflated;
        git_zstream stream;
  
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        bound = git_deflate_bound(&stream, size);
        deflated = xmalloc(bound);
@@@ -2187,8 -2184,8 +2186,8 @@@ int diff_filespec_is_binary(struct diff
                        one->is_binary = one->driver->binary;
                else {
                        if (!one->data && DIFF_FILE_VALID(one))
 -                              diff_populate_filespec(one, 0);
 -                      if (one->data)
 +                              diff_populate_filespec(one, CHECK_BINARY);
 +                      if (one->is_binary == -1 && one->data)
                                one->is_binary = buffer_is_binary(one->data,
                                                one->size);
                        if (one->is_binary == -1)
@@@ -2323,19 -2320,6 +2322,19 @@@ static void builtin_diff(const char *na
        } else if (!DIFF_OPT_TST(o, TEXT) &&
            ( (!textconv_one && diff_filespec_is_binary(one)) ||
              (!textconv_two && diff_filespec_is_binary(two)) )) {
 +              if (!one->data && !two->data &&
 +                  S_ISREG(one->mode) && S_ISREG(two->mode) &&
 +                  !DIFF_OPT_TST(o, BINARY)) {
 +                      if (!hashcmp(one->sha1, two->sha1)) {
 +                              if (must_show_header)
 +                                      fprintf(o->file, "%s", header.buf);
 +                              goto free_ab_and_return;
 +                      }
 +                      fprintf(o->file, "%s", header.buf);
 +                      fprintf(o->file, "%sBinary files %s and %s differ\n",
 +                              line_prefix, lbl[0], lbl[1]);
 +                      goto free_ab_and_return;
 +              }
                if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                        die("unable to read files to diff");
                /* Quite common confusing case */
        } else {
                /* Crazy xdl interfaces.. */
                const char *diffopts = getenv("GIT_DIFF_OPTS");
 +              const char *v;
                xpparam_t xpp;
                xdemitconf_t xecfg;
                struct emit_callback ecbdata;
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
 -              else if (starts_with(diffopts, "--unified="))
 -                      xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 -              else if (starts_with(diffopts, "-u"))
 -                      xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
 +              else if (skip_prefix(diffopts, "--unified=", &v))
 +                      xecfg.ctxlen = strtoul(v, NULL, 10);
 +              else if (skip_prefix(diffopts, "-u", &v))
 +                      xecfg.ctxlen = strtoul(v, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
                xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
@@@ -2680,9 -2663,8 +2679,9 @@@ static int diff_populate_gitlink(struc
   * grab the data for the blob (or file) for our own in-core comparison.
   * diff_filespec has data and size fields for this purpose.
   */
 -int diff_populate_filespec(struct diff_filespec *s, int size_only)
 +int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
  {
 +      int size_only = flags & CHECK_SIZE_ONLY;
        int err = 0;
        /*
         * demote FAIL to WARN to allow inspecting the situation
                }
                if (size_only)
                        return 0;
 +              if ((flags & CHECK_BINARY) &&
 +                  s->size > big_file_threshold && s->is_binary == -1) {
 +                      s->is_binary = 1;
 +                      return 0;
 +              }
                fd = open(s->path, O_RDONLY);
                if (fd < 0)
                        goto err_empty;
        }
        else {
                enum object_type type;
 -              if (size_only) {
 +              if (size_only || (flags & CHECK_BINARY)) {
                        type = sha1_object_info(s->sha1, &s->size);
                        if (type < 0)
                                die("unable to read %s", sha1_to_hex(s->sha1));
 -              } else {
 -                      s->data = read_sha1_file(s->sha1, &type, &s->size);
 -                      if (!s->data)
 -                              die("unable to read %s", sha1_to_hex(s->sha1));
 -                      s->should_free = 1;
 +                      if (size_only)
 +                              return 0;
 +                      if (s->size > big_file_threshold && s->is_binary == -1) {
 +                              s->is_binary = 1;
 +                              return 0;
 +                      }
                }
 +              s->data = read_sha1_file(s->sha1, &type, &s->size);
 +              if (!s->data)
 +                      die("unable to read %s", sha1_to_hex(s->sha1));
 +              s->should_free = 1;
        }
        return 0;
  }
@@@ -2907,16 -2879,6 +2906,16 @@@ static struct diff_tempfile *prepare_te
        return temp;
  }
  
 +static void add_external_diff_name(struct argv_array *argv,
 +                                 const char *name,
 +                                 struct diff_filespec *df)
 +{
 +      struct diff_tempfile *temp = prepare_temp_file(name, df);
 +      argv_array_push(argv, temp->name);
 +      argv_array_push(argv, temp->hex);
 +      argv_array_push(argv, temp->mode);
 +}
 +
  /* An external diff command takes:
   *
   * diff-cmd name infile1 infile1-sha1 infile1-mode \
@@@ -2933,32 -2895,48 +2932,32 @@@ static void run_external_diff(const cha
                              struct diff_options *o)
  {
        struct argv_array argv = ARGV_ARRAY_INIT;
 -      int retval;
 +      struct argv_array env = ARGV_ARRAY_INIT;
        struct diff_queue_struct *q = &diff_queued_diff;
 -      const char *env[3] = { NULL };
 -      char env_counter[50];
 -      char env_total[50];
 +
 +      argv_array_push(&argv, pgm);
 +      argv_array_push(&argv, name);
  
        if (one && two) {
 -              struct diff_tempfile *temp_one, *temp_two;
 -              const char *othername = (other ? other : name);
 -              temp_one = prepare_temp_file(name, one);
 -              temp_two = prepare_temp_file(othername, two);
 -              argv_array_push(&argv, pgm);
 -              argv_array_push(&argv, name);
 -              argv_array_push(&argv, temp_one->name);
 -              argv_array_push(&argv, temp_one->hex);
 -              argv_array_push(&argv, temp_one->mode);
 -              argv_array_push(&argv, temp_two->name);
 -              argv_array_push(&argv, temp_two->hex);
 -              argv_array_push(&argv, temp_two->mode);
 -              if (other) {
 +              add_external_diff_name(&argv, name, one);
 +              if (!other)
 +                      add_external_diff_name(&argv, name, two);
 +              else {
 +                      add_external_diff_name(&argv, other, two);
                        argv_array_push(&argv, other);
                        argv_array_push(&argv, xfrm_msg);
                }
 -      } else {
 -              argv_array_push(&argv, pgm);
 -              argv_array_push(&argv, name);
        }
 -      fflush(NULL);
  
 -      env[0] = env_counter;
 -      snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
 -               ++o->diff_path_counter);
 -      env[1] = env_total;
 -      snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
 +      argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
 +      argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
 +
 +      if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
 +              die(_("external diff died, stopping at %s"), name);
  
 -      retval = run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
        argv_array_clear(&argv);
 -      if (retval) {
 -              fprintf(stderr, "external diff died, stopping at %s.\n", name);
 -              exit(1);
 -      }
 +      argv_array_clear(&env);
  }
  
  static int similarity_index(struct diff_filepair *p)
@@@ -3226,7 -3204,6 +3225,7 @@@ void diff_setup(struct diff_options *op
        options->context = diff_context_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
 +      /* pathchange left =NULL by default */
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->use_color = diff_use_color_default;
@@@ -3418,10 -3395,12 +3417,10 @@@ int parse_long_opt(const char *opt, con
                   const char **optarg)
  {
        const char *arg = argv[0];
 -      if (arg[0] != '-' || arg[1] != '-')
 +      if (!skip_prefix(arg, "--", &arg))
                return 0;
 -      arg += strlen("--");
 -      if (!starts_with(arg, opt))
 +      if (!skip_prefix(arg, opt, &arg))
                return 0;
 -      arg += strlen(opt);
        if (*arg == '=') { /* stuck form: --option=value */
                *optarg = arg + 1;
                return 1;
@@@ -3445,13 -3424,13 +3444,13 @@@ static int stat_opt(struct diff_option
        int count = options->stat_count;
        int argcount = 1;
  
 -      arg += strlen("--stat");
 +      if (!skip_prefix(arg, "--stat", &arg))
 +              die("BUG: stat option does not begin with --stat: %s", arg);
        end = (char *)arg;
  
        switch (*arg) {
        case '-':
 -              if (starts_with(arg, "-width")) {
 -                      arg += strlen("-width");
 +              if (skip_prefix(arg, "-width", &arg)) {
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
 -              } else if (starts_with(arg, "-name-width")) {
 -                      arg += strlen("-name-width");
 +              } else if (skip_prefix(arg, "-name-width", &arg)) {
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
 -              } else if (starts_with(arg, "-graph-width")) {
 -                      arg += strlen("-graph-width");
 +              } else if (skip_prefix(arg, "-graph-width", &arg)) {
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
 -              } else if (starts_with(arg, "-count")) {
 -                      arg += strlen("-count");
 +              } else if (skip_prefix(arg, "-count", &arg)) {
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
@@@ -3631,17 -3613,17 +3630,17 @@@ int diff_opt_parse(struct diff_options 
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
                return parse_dirstat_opt(options, "");
 -      else if (starts_with(arg, "-X"))
 -              return parse_dirstat_opt(options, arg + 2);
 -      else if (starts_with(arg, "--dirstat="))
 -              return parse_dirstat_opt(options, arg + 10);
 +      else if (skip_prefix(arg, "-X", &arg))
 +              return parse_dirstat_opt(options, arg);
 +      else if (skip_prefix(arg, "--dirstat=", &arg))
 +              return parse_dirstat_opt(options, arg);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
        else if (!strcmp(arg, "--dirstat-by-file"))
                return parse_dirstat_opt(options, "files");
 -      else if (starts_with(arg, "--dirstat-by-file=")) {
 +      else if (skip_prefix(arg, "--dirstat-by-file=", &arg)) {
                parse_dirstat_opt(options, "files");
 -              return parse_dirstat_opt(options, arg + 18);
 +              return parse_dirstat_opt(options, arg);
        }
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
                DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
 -      else if (starts_with(arg, "--relative=")) {
 +      else if (skip_prefix(arg, "--relative=", &arg)) {
                DIFF_OPT_SET(options, RELATIVE_NAME);
 -              options->prefix = arg + 11;
 +              options->prefix = arg;
        }
  
        /* xdiff options */
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                options->use_color = 1;
 -      else if (starts_with(arg, "--color=")) {
 -              int value = git_config_colorbool(NULL, arg+8);
 +      else if (skip_prefix(arg, "--color=", &arg)) {
 +              int value = git_config_colorbool(NULL, arg);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
                options->use_color = value;
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
 -      else if (starts_with(arg, "--color-words=")) {
 +      else if (skip_prefix(arg, "--color-words=", &arg)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
 -              options->word_regex = arg + 14;
 +              options->word_regex = arg;
        }
        else if (!strcmp(arg, "--word-diff")) {
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
        }
 -      else if (starts_with(arg, "--word-diff=")) {
 -              const char *type = arg + 12;
 -              if (!strcmp(type, "plain"))
 +      else if (skip_prefix(arg, "--word-diff=", &arg)) {
 +              if (!strcmp(arg, "plain"))
                        options->word_diff = DIFF_WORDS_PLAIN;
 -              else if (!strcmp(type, "color")) {
 +              else if (!strcmp(arg, "color")) {
                        options->use_color = 1;
                        options->word_diff = DIFF_WORDS_COLOR;
                }
 -              else if (!strcmp(type, "porcelain"))
 +              else if (!strcmp(arg, "porcelain"))
                        options->word_diff = DIFF_WORDS_PORCELAIN;
 -              else if (!strcmp(type, "none"))
 +              else if (!strcmp(arg, "none"))
                        options->word_diff = DIFF_WORDS_NONE;
                else
 -                      die("bad --word-diff argument: %s", type);
 +                      die("bad --word-diff argument: %s", arg);
        }
        else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
                if (options->word_diff == DIFF_WORDS_NONE)
        else if (!strcmp(arg, "--ignore-submodules")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
 -      } else if (starts_with(arg, "--ignore-submodules=")) {
 +      } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
 -              handle_ignore_submodules_arg(options, arg + 20);
 +              handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
 -      else if (starts_with(arg, "--submodule="))
 -              return parse_submodule_opt(options, arg + 12);
 +      else if (skip_prefix(arg, "--submodule=", &arg))
 +              return parse_submodule_opt(options, arg);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
        }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
 -      else if (starts_with(arg, "--abbrev=")) {
 -              options->abbrev = strtoul(arg + 9, NULL, 10);
 +      else if (skip_prefix(arg, "--abbrev=", &arg)) {
 +              options->abbrev = strtoul(arg, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
                else if (40 < options->abbrev)
@@@ -3923,13 -3906,16 +3922,13 @@@ static int diff_scoreopt_parse(const ch
        cmd = *opt++;
        if (cmd == '-') {
                /* convert the long-form arguments into short-form versions */
 -              if (starts_with(opt, "break-rewrites")) {
 -                      opt += strlen("break-rewrites");
 +              if (skip_prefix(opt, "break-rewrites", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'B';
 -              } else if (starts_with(opt, "find-copies")) {
 -                      opt += strlen("find-copies");
 +              } else if (skip_prefix(opt, "find-copies", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'C';
 -              } else if (starts_with(opt, "find-renames")) {
 -                      opt += strlen("find-renames");
 +              } else if (skip_prefix(opt, "find-renames", &opt)) {
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'M';
                }
@@@ -4541,7 -4527,7 +4540,7 @@@ void diff_flush(struct diff_options *op
                        show_stats(&diffstat, options);
                if (output_format & DIFF_FORMAT_SHORTSTAT)
                        show_shortstats(&diffstat, options);
 -              if (output_format & DIFF_FORMAT_DIRSTAT)
 +              if (output_format & DIFF_FORMAT_DIRSTAT && dirstat_by_line)
                        show_dirstat_by_line(&diffstat, options);
                free_diffstat_info(&diffstat);
                separator++;
@@@ -4711,8 -4697,8 +4710,8 @@@ static int diff_filespec_check_stat_unm
            !DIFF_FILE_VALID(p->two) ||
            (p->one->sha1_valid && p->two->sha1_valid) ||
            (p->one->mode != p->two->mode) ||
 -          diff_populate_filespec(p->one, 1) ||
 -          diff_populate_filespec(p->two, 1) ||
 +          diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
 +          diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
            (p->one->size != p->two->size) ||
            !diff_filespec_is_identical(p->one, p->two)) /* (2) */
                p->skip_stat_unmatch_result = 1;
@@@ -4765,7 -4751,6 +4764,7 @@@ void diffcore_fix_diff_index(struct dif
  
  void diffcore_std(struct diff_options *options)
  {
 +      /* NOTE please keep the following in sync with diff_tree_combined() */
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (!options->found_follow) {
@@@ -4954,7 -4939,7 +4953,7 @@@ static char *run_textconv(const char *p
        struct diff_tempfile *temp;
        const char *argv[3];
        const char **arg = argv;
 -      struct child_process child;
 +      struct child_process child = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
  
        *arg++ = temp->name;
        *arg = NULL;
  
 -      memset(&child, 0, sizeof(child));
        child.use_shell = 1;
        child.argv = argv;
        child.out = -1;
diff --combined fast-import.c
index b82fcdd24e34d9191aae33fed4e8afb3ca4b6f6a,f757ef6b90a16794e64894683977cda15df92ca7..e78ca107b3d66d7e537c86eb10dc7503be8681b9
@@@ -153,7 -153,6 +153,7 @@@ Format of STDIN stream
  
  #include "builtin.h"
  #include "cache.h"
 +#include "lockfile.h"
  #include "object.h"
  #include "blob.h"
  #include "tree.h"
@@@ -249,7 -248,6 +249,7 @@@ struct branch 
        uintmax_t last_commit;
        uintmax_t num_notes;
        unsigned active : 1;
 +      unsigned delete : 1;
        unsigned pack_id : PACK_ID_BITS;
        unsigned char sha1[20];
  };
@@@ -372,8 -370,8 +372,8 @@@ static volatile sig_atomic_t checkpoint
  static int cat_blob_fd = STDOUT_FILENO;
  
  static void parse_argv(void);
 -static void parse_cat_blob(void);
 -static void parse_ls(struct branch *b);
 +static void parse_cat_blob(const char *p);
 +static void parse_ls(const char *p, struct branch *b);
  
  static void write_branch_report(FILE *rpt, struct branch *b)
  {
@@@ -879,7 -877,7 +879,7 @@@ static void start_packfile(void
        pack_size = sizeof(hdr);
        object_count = 0;
  
 -      all_packs = xrealloc(all_packs, sizeof(*all_packs) * (pack_id + 1));
 +      REALLOC_ARRAY(all_packs, pack_id + 1);
        all_packs[pack_id] = p;
  }
  
@@@ -947,15 -945,10 +947,15 @@@ static void unkeep_all_packs(void
  
  static void end_packfile(void)
  {
 -      struct packed_git *old_p = pack_data, *new_p;
 +      static int running;
  
 +      if (running || !pack_data)
 +              return;
 +
 +      running = 1;
        clear_delta_base_cache();
        if (object_count) {
 +              struct packed_git *new_p;
                unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                pack_id++;
        }
        else {
 -              close(old_p->pack_fd);
 -              unlink_or_warn(old_p->pack_name);
 +              close(pack_data->pack_fd);
 +              unlink_or_warn(pack_data->pack_name);
        }
 -      free(old_p);
 +      free(pack_data);
 +      pack_data = NULL;
 +      running = 0;
  
        /* We can't carry a delta across packfiles. */
        strbuf_release(&last_blob.data);
@@@ -1062,7 -1053,6 +1062,6 @@@ static int store_object
        } else
                delta = NULL;
  
-       memset(&s, 0, sizeof(s));
        git_deflate_init(&s, pack_compression_level);
        if (delta) {
                s.next_in = delta;
                        free(delta);
                        delta = NULL;
  
-                       memset(&s, 0, sizeof(s));
                        git_deflate_init(&s, pack_compression_level);
                        s.next_in = (void *)dat->buf;
                        s.avail_in = dat->len;
@@@ -1190,7 -1179,6 +1188,6 @@@ static void stream_blob(uintmax_t len, 
  
        crc32_begin(pack_file);
  
-       memset(&s, 0, sizeof(s));
        git_deflate_init(&s, pack_compression_level);
  
        hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
@@@ -1427,7 -1415,7 +1424,7 @@@ static void mktree(struct tree_content 
  
  static void store_tree(struct tree_entry *root)
  {
 -      struct tree_content *t = root->tree;
 +      struct tree_content *t;
        unsigned int i, j, del;
        struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
        struct object_entry *le = NULL;
        if (!is_null_sha1(root->versions[1].sha1))
                return;
  
 +      if (!root->tree)
 +              load_tree(root);
 +      t = root->tree;
 +
        for (i = 0; i < t->entry_count; i++) {
                if (t->entries[i]->tree)
                        store_tree(t->entries[i]);
@@@ -1691,44 -1675,36 +1688,44 @@@ found_entry
  static int update_branch(struct branch *b)
  {
        static const char *msg = "fast-import";
 -      struct ref_lock *lock;
 +      struct ref_transaction *transaction;
        unsigned char old_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
 -      if (is_null_sha1(b->sha1))
 -              return 0;
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
 -      lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
 -      if (!lock)
 -              return error("Unable to lock %s", b->name);
 +      if (is_null_sha1(b->sha1)) {
 +              if (b->delete)
 +                      delete_ref(b->name, old_sha1, 0);
 +              return 0;
 +      }
        if (!force_update && !is_null_sha1(old_sha1)) {
                struct commit *old_cmit, *new_cmit;
  
                old_cmit = lookup_commit_reference_gently(old_sha1, 0);
                new_cmit = lookup_commit_reference_gently(b->sha1, 0);
 -              if (!old_cmit || !new_cmit) {
 -                      unlock_ref(lock);
 +              if (!old_cmit || !new_cmit)
                        return error("Branch %s is missing commits.", b->name);
 -              }
  
                if (!in_merge_bases(old_cmit, new_cmit)) {
 -                      unlock_ref(lock);
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
                                b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
                        return -1;
                }
        }
 -      if (write_ref_sha1(lock, b->sha1, msg) < 0)
 -              return error("Unable to update %s", b->name);
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction ||
 +          ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
 +                                 0, msg, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
 +              ref_transaction_free(transaction);
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +              return -1;
 +      }
 +      ref_transaction_free(transaction);
 +      strbuf_release(&err);
        return 0;
  }
  
@@@ -1747,32 -1723,15 +1744,32 @@@ static void dump_tags(void
  {
        static const char *msg = "fast-import";
        struct tag *t;
 -      struct ref_lock *lock;
 -      char ref_name[PATH_MAX];
 +      struct strbuf ref_name = STRBUF_INIT;
 +      struct strbuf err = STRBUF_INIT;
 +      struct ref_transaction *transaction;
  
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction) {
 +              failure |= error("%s", err.buf);
 +              goto cleanup;
 +      }
        for (t = first_tag; t; t = t->next_tag) {
 -              sprintf(ref_name, "tags/%s", t->name);
 -              lock = lock_ref_sha1(ref_name, NULL);
 -              if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
 -                      failure |= error("Unable to update %s", ref_name);
 +              strbuf_reset(&ref_name);
 +              strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 +
 +              if (ref_transaction_update(transaction, ref_name.buf,
 +                                         t->sha1, NULL, 0, msg, &err)) {
 +                      failure |= error("%s", err.buf);
 +                      goto cleanup;
 +              }
        }
 +      if (ref_transaction_commit(transaction, &err))
 +              failure |= error("%s", err.buf);
 +
 + cleanup:
 +      ref_transaction_free(transaction);
 +      strbuf_release(&ref_name);
 +      strbuf_release(&err);
  }
  
  static void dump_marks_helper(FILE *f,
  static void dump_marks(void)
  {
        static struct lock_file mark_lock;
 -      int mark_fd;
        FILE *f;
  
        if (!export_marks_file)
                return;
  
 -      mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
 -      if (mark_fd < 0) {
 +      if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
                failure |= error("Unable to write marks file %s: %s",
                        export_marks_file, strerror(errno));
                return;
        }
  
 -      f = fdopen(mark_fd, "w");
 +      f = fdopen_lock_file(&mark_lock, "w");
        if (!f) {
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                return;
        }
  
 -      /*
 -       * Since the lock file was fdopen()'ed, it should not be close()'ed.
 -       * Assign -1 to the lock file descriptor so that commit_lock_file()
 -       * won't try to close() it.
 -       */
 -      mark_lock.fd = -1;
 -
        dump_marks_helper(f, 0, marks);
 -      if (ferror(f) || fclose(f)) {
 -              int saved_errno = errno;
 -              rollback_lock_file(&mark_lock);
 -              failure |= error("Unable to write marks file %s: %s",
 -                      export_marks_file, strerror(saved_errno));
 -              return;
 -      }
 -
        if (commit_lock_file(&mark_lock)) {
 -              int saved_errno = errno;
 -              rollback_lock_file(&mark_lock);
                failure |= error("Unable to commit marks file %s: %s",
 -                      export_marks_file, strerror(saved_errno));
 +                      export_marks_file, strerror(errno));
                return;
        }
  }
@@@ -1876,8 -1854,6 +1873,8 @@@ static int read_next_command(void
        }
  
        for (;;) {
 +              const char *p;
 +
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
 -              if (starts_with(command_buf.buf, "cat-blob ")) {
 -                      parse_cat_blob();
 +              if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
 +                      parse_cat_blob(p);
                        continue;
                }
                if (command_buf.buf[0] == '#')
@@@ -1929,9 -1905,8 +1926,9 @@@ static void skip_optional_lf(void
  
  static void parse_mark(void)
  {
 -      if (starts_with(command_buf.buf, "mark :")) {
 -              next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
 +      const char *v;
 +      if (skip_prefix(command_buf.buf, "mark :", &v)) {
 +              next_mark = strtoumax(v, NULL, 10);
                read_next_command();
        }
        else
  
  static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
  {
 +      const char *data;
        strbuf_reset(sb);
  
 -      if (!starts_with(command_buf.buf, "data "))
 +      if (!skip_prefix(command_buf.buf, "data ", &data))
                die("Expected 'data n' command, found: %s", command_buf.buf);
  
 -      if (starts_with(command_buf.buf + 5, "<<")) {
 -              char *term = xstrdup(command_buf.buf + 5 + 2);
 -              size_t term_len = command_buf.len - 5 - 2;
 +      if (skip_prefix(data, "<<", &data)) {
 +              char *term = xstrdup(data);
 +              size_t term_len = command_buf.len - (data - command_buf.buf);
  
                strbuf_detach(&command_buf, NULL);
                for (;;) {
                free(term);
        }
        else {
 -              uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
 +              uintmax_t len = strtoumax(data, NULL, 10);
                size_t n = 0, length = (size_t)len;
  
                if (limit && limit < len) {
        return 1;
  }
  
 -static int validate_raw_date(const char *src, char *result, int maxlen)
 +static int validate_raw_date(const char *src, struct strbuf *result)
  {
        const char *orig_src = src;
        char *endp;
                return -1;
  
        num = strtoul(src + 1, &endp, 10);
 -      if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
 -          1400 < num)
 +      if (errno || endp == src + 1 || *endp || 1400 < num)
                return -1;
  
 -      strcpy(result, orig_src);
 +      strbuf_addstr(result, orig_src);
        return 0;
  }
  
@@@ -2015,7 -1990,7 +2012,7 @@@ static char *parse_ident(const char *bu
  {
        const char *ltgt;
        size_t name_len;
 -      char *ident;
 +      struct strbuf ident = STRBUF_INIT;
  
        /* ensure there is a space delimiter even if there is no name */
        if (*buf == '<')
                die("Missing space after > in ident string: %s", buf);
        ltgt++;
        name_len = ltgt - buf;
 -      ident = xmalloc(name_len + 24);
 -      strncpy(ident, buf, name_len);
 +      strbuf_add(&ident, buf, name_len);
  
        switch (whenspec) {
        case WHENSPEC_RAW:
 -              if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
 +              if (validate_raw_date(ltgt, &ident) < 0)
                        die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_RFC2822:
 -              if (parse_date(ltgt, ident + name_len, 24) < 0)
 +              if (parse_date(ltgt, &ident) < 0)
                        die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_NOW:
                if (strcmp("now", ltgt))
                        die("Date in ident must be 'now': %s", buf);
 -              datestamp(ident + name_len, 24);
 +              datestamp(&ident);
                break;
        }
  
 -      return ident;
 +      return strbuf_detach(&ident, NULL);
  }
  
  static void parse_and_store_blob(
@@@ -2282,14 -2258,15 +2279,14 @@@ static uintmax_t parse_mark_ref_space(c
        char *end;
  
        mark = parse_mark_ref(*p, &end);
 -      if (*end != ' ')
 +      if (*end++ != ' ')
                die("Missing space after mark: %s", command_buf.buf);
        *p = end;
        return mark;
  }
  
 -static void file_change_m(struct branch *b)
 +static void file_change_m(const char *p, struct branch *b)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
        struct object_entry *oe;
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
 -      } else if (starts_with(p, "inline ")) {
 +      } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
 -              p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
 -              if (*p != ' ')
 +              if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
 -      assert(*p == ' ');
 -      p++;  /* skip space */
  
        strbuf_reset(&uq);
        if (!unquote_c_style(&uq, p, &endp)) {
        }
  
        /* Git does not track empty, non-toplevel directories. */
 -      if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
 +      if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) {
                tree_content_remove(&b->branch_tree, p, NULL, 0);
                return;
        }
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
  }
  
 -static void file_change_d(struct branch *b)
 +static void file_change_d(const char *p, struct branch *b)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
  
        tree_content_remove(&b->branch_tree, p, NULL, 1);
  }
  
 -static void file_change_cr(struct branch *b, int rename)
 +static void file_change_cr(const char *s, struct branch *b, int rename)
  {
 -      const char *s, *d;
 +      const char *d;
        static struct strbuf s_uq = STRBUF_INIT;
        static struct strbuf d_uq = STRBUF_INIT;
        const char *endp;
        struct tree_entry leaf;
  
 -      s = command_buf.buf + 2;
        strbuf_reset(&s_uq);
        if (!unquote_c_style(&s_uq, s, &endp)) {
                if (*endp != ' ')
                leaf.tree);
  }
  
 -static void note_change_n(struct branch *b, unsigned char *old_fanout)
 +static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        struct object_entry *oe;
        struct branch *s;
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
 -      } else if (starts_with(p, "inline ")) {
 +      } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
 -              p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
 -              if (*p != ' ')
 +              if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
 -      assert(*p == ' ');
 -      p++;  /* skip space */
  
        /* <commit-ish> */
        s = lookup_branch(p);
@@@ -2592,7 -2578,7 +2589,7 @@@ static int parse_from(struct branch *b
        const char *from;
        struct branch *s;
  
 -      if (!starts_with(command_buf.buf, "from "))
 +      if (!skip_prefix(command_buf.buf, "from ", &from))
                return 0;
  
        if (b->branch_tree.tree) {
                b->branch_tree.tree = NULL;
        }
  
 -      from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (b == s)
                die("Can't create a branch from itself: %s", b->name);
                        free(buf);
                } else
                        parse_from_existing(b);
 -      } else if (!get_sha1(from, b->sha1))
 +      } else if (!get_sha1(from, b->sha1)) {
                parse_from_existing(b);
 +              if (is_null_sha1(b->sha1))
 +                      b->delete = 1;
 +      }
        else
                die("Invalid ref name or SHA1 expression: %s", from);
  
@@@ -2640,7 -2624,8 +2637,7 @@@ static struct hash_list *parse_merge(un
        struct branch *s;
  
        *count = 0;
 -      while (starts_with(command_buf.buf, "merge ")) {
 -              from = strchr(command_buf.buf, ' ') + 1;
 +      while (skip_prefix(command_buf.buf, "merge ", &from)) {
                n = xmalloc(sizeof(*n));
                s = lookup_branch(from);
                if (s)
        return list;
  }
  
 -static void parse_new_commit(void)
 +static void parse_new_commit(const char *arg)
  {
        static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
 -      char *sp;
        char *author = NULL;
        char *committer = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
        unsigned char prev_fanout, new_fanout;
 +      const char *v;
  
 -      /* Obtain the branch name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
 -      b = lookup_branch(sp);
 +      b = lookup_branch(arg);
        if (!b)
 -              b = new_branch(sp);
 +              b = new_branch(arg);
  
        read_next_command();
        parse_mark();
 -      if (starts_with(command_buf.buf, "author ")) {
 -              author = parse_ident(command_buf.buf + 7);
 +      if (skip_prefix(command_buf.buf, "author ", &v)) {
 +              author = parse_ident(v);
                read_next_command();
        }
 -      if (starts_with(command_buf.buf, "committer ")) {
 -              committer = parse_ident(command_buf.buf + 10);
 +      if (skip_prefix(command_buf.buf, "committer ", &v)) {
 +              committer = parse_ident(v);
                read_next_command();
        }
        if (!committer)
  
        /* file_change* */
        while (command_buf.len > 0) {
 -              if (starts_with(command_buf.buf, "M "))
 -                      file_change_m(b);
 -              else if (starts_with(command_buf.buf, "D "))
 -                      file_change_d(b);
 -              else if (starts_with(command_buf.buf, "R "))
 -                      file_change_cr(b, 1);
 -              else if (starts_with(command_buf.buf, "C "))
 -                      file_change_cr(b, 0);
 -              else if (starts_with(command_buf.buf, "N "))
 -                      note_change_n(b, &prev_fanout);
 +              if (skip_prefix(command_buf.buf, "M ", &v))
 +                      file_change_m(v, b);
 +              else if (skip_prefix(command_buf.buf, "D ", &v))
 +                      file_change_d(v, b);
 +              else if (skip_prefix(command_buf.buf, "R ", &v))
 +                      file_change_cr(v, b, 1);
 +              else if (skip_prefix(command_buf.buf, "C ", &v))
 +                      file_change_cr(v, b, 0);
 +              else if (skip_prefix(command_buf.buf, "N ", &v))
 +                      note_change_n(v, b, &prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
 -              else if (starts_with(command_buf.buf, "ls "))
 -                      parse_ls(b);
 +              else if (skip_prefix(command_buf.buf, "ls ", &v))
 +                      parse_ls(v, b);
                else {
                        unread_command_buf = 1;
                        break;
        b->last_commit = object_count_by_type[OBJ_COMMIT];
  }
  
 -static void parse_new_tag(void)
 +static void parse_new_tag(const char *arg)
  {
        static struct strbuf msg = STRBUF_INIT;
 -      char *sp;
        const char *from;
        char *tagger;
        struct branch *s;
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
        enum object_type type;
 +      const char *v;
  
 -      /* Obtain the new tag name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
        t = pool_alloc(sizeof(struct tag));
        memset(t, 0, sizeof(struct tag));
 -      t->name = pool_strdup(sp);
 +      t->name = pool_strdup(arg);
        if (last_tag)
                last_tag->next_tag = t;
        else
        read_next_command();
  
        /* from ... */
 -      if (!starts_with(command_buf.buf, "from "))
 +      if (!skip_prefix(command_buf.buf, "from ", &from))
                die("Expected from command, got %s", command_buf.buf);
 -      from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (s) {
                if (is_null_sha1(s->sha1))
        read_next_command();
  
        /* tagger ... */
 -      if (starts_with(command_buf.buf, "tagger ")) {
 -              tagger = parse_ident(command_buf.buf + 7);
 +      if (skip_prefix(command_buf.buf, "tagger ", &v)) {
 +              tagger = parse_ident(v);
                read_next_command();
        } else
                tagger = NULL;
                t->pack_id = pack_id;
  }
  
 -static void parse_reset_branch(void)
 +static void parse_reset_branch(const char *arg)
  {
        struct branch *b;
 -      char *sp;
  
 -      /* Obtain the branch name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
 -      b = lookup_branch(sp);
 +      b = lookup_branch(arg);
        if (b) {
                hashclr(b->sha1);
                hashclr(b->branch_tree.versions[0].sha1);
                }
        }
        else
 -              b = new_branch(sp);
 +              b = new_branch(arg);
        read_next_command();
        parse_from(b);
        if (command_buf.len > 0)
@@@ -2922,12 -2915,14 +2919,12 @@@ static void cat_blob(struct object_entr
                free(buf);
  }
  
 -static void parse_cat_blob(void)
 +static void parse_cat_blob(const char *p)
  {
 -      const char *p;
        struct object_entry *oe = oe;
        unsigned char sha1[20];
  
        /* cat-blob SP <object> LF */
 -      p = command_buf.buf + strlen("cat-blob ");
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_eol(p));
                if (!oe)
@@@ -3010,8 -3005,6 +3007,8 @@@ static struct object_entry *parse_treei
                        die("Invalid dataref: %s", command_buf.buf);
                e = find_object(sha1);
                *p += 40;
 +              if (*(*p)++ != ' ')
 +                      die("Missing space after tree-ish: %s", command_buf.buf);
        }
  
        while (!e || e->type != OBJ_TREE)
@@@ -3046,12 -3039,14 +3043,12 @@@ static void print_ls(int mode, const un
        cat_blob_write(line.buf, line.len);
  }
  
 -static void parse_ls(struct branch *b)
 +static void parse_ls(const char *p, struct branch *b)
  {
 -      const char *p;
        struct tree_entry *root = NULL;
        struct tree_entry leaf = {NULL};
  
        /* ls SP (<tree-ish> SP)? <path> */
 -      p = command_buf.buf + strlen("ls ");
        if (*p == '"') {
                if (!b)
                        die("Not in a commit: %s", command_buf.buf);
                if (!is_null_sha1(root->versions[1].sha1))
                        root->versions[1].mode = S_IFDIR;
                load_tree(root);
 -              if (*p++ != ' ')
 -                      die("Missing space after tree-ish: %s", command_buf.buf);
        }
        if (*p == '"') {
                static struct strbuf uq = STRBUF_INIT;
@@@ -3200,9 -3197,9 +3197,9 @@@ static void option_export_pack_edges(co
  
  static int parse_one_option(const char *option)
  {
 -      if (starts_with(option, "max-pack-size=")) {
 +      if (skip_prefix(option, "max-pack-size=", &option)) {
                unsigned long v;
 -              if (!git_parse_ulong(option + 14, &v))
 +              if (!git_parse_ulong(option, &v))
                        return 0;
                if (v < 8192) {
                        warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
                        v = 1024 * 1024;
                }
                max_packsize = v;
 -      } else if (starts_with(option, "big-file-threshold=")) {
 +      } else if (skip_prefix(option, "big-file-threshold=", &option)) {
                unsigned long v;
 -              if (!git_parse_ulong(option + 19, &v))
 +              if (!git_parse_ulong(option, &v))
                        return 0;
                big_file_threshold = v;
 -      } else if (starts_with(option, "depth=")) {
 -              option_depth(option + 6);
 -      } else if (starts_with(option, "active-branches=")) {
 -              option_active_branches(option + 16);
 -      } else if (starts_with(option, "export-pack-edges=")) {
 -              option_export_pack_edges(option + 18);
 +      } else if (skip_prefix(option, "depth=", &option)) {
 +              option_depth(option);
 +      } else if (skip_prefix(option, "active-branches=", &option)) {
 +              option_active_branches(option);
 +      } else if (skip_prefix(option, "export-pack-edges=", &option)) {
 +              option_export_pack_edges(option);
        } else if (starts_with(option, "quiet")) {
                show_stats = 0;
        } else if (starts_with(option, "stats")) {
  
  static int parse_one_feature(const char *feature, int from_stream)
  {
 -      if (starts_with(feature, "date-format=")) {
 -              option_date_format(feature + 12);
 -      } else if (starts_with(feature, "import-marks=")) {
 -              option_import_marks(feature + 13, from_stream, 0);
 -      } else if (starts_with(feature, "import-marks-if-exists=")) {
 -              option_import_marks(feature + strlen("import-marks-if-exists="),
 -                                      from_stream, 1);
 -      } else if (starts_with(feature, "export-marks=")) {
 -              option_export_marks(feature + 13);
 +      const char *arg;
 +
 +      if (skip_prefix(feature, "date-format=", &arg)) {
 +              option_date_format(arg);
 +      } else if (skip_prefix(feature, "import-marks=", &arg)) {
 +              option_import_marks(arg, from_stream, 0);
 +      } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
 +              option_import_marks(arg, from_stream, 1);
 +      } else if (skip_prefix(feature, "export-marks=", &arg)) {
 +              option_export_marks(arg);
        } else if (!strcmp(feature, "cat-blob")) {
                ; /* Don't die - this feature is supported */
        } else if (!strcmp(feature, "relative-marks")) {
        return 1;
  }
  
 -static void parse_feature(void)
 +static void parse_feature(const char *feature)
  {
 -      char *feature = command_buf.buf + 8;
 -
        if (seen_data_command)
                die("Got feature command '%s' after data command", feature);
  
        die("This version of fast-import does not support feature %s.", feature);
  }
  
 -static void parse_option(void)
 +static void parse_option(const char *option)
  {
 -      char *option = command_buf.buf + 11;
 -
        if (seen_data_command)
                die("Got option command '%s' after data command", option);
  
        die("This version of fast-import does not support option: %s", option);
  }
  
 -static int git_pack_config(const char *k, const char *v, void *cb)
 +static void git_pack_config(void)
  {
 -      if (!strcmp(k, "pack.depth")) {
 -              max_depth = git_config_int(k, v);
 +      int indexversion_value;
 +      unsigned long packsizelimit_value;
 +
 +      if (!git_config_get_ulong("pack.depth", &max_depth)) {
                if (max_depth > MAX_DEPTH)
                        max_depth = MAX_DEPTH;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.compression")) {
 -              int level = git_config_int(k, v);
 -              if (level == -1)
 -                      level = Z_DEFAULT_COMPRESSION;
 -              else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                      die("bad pack compression level %d", level);
 -              pack_compression_level = level;
 +      if (!git_config_get_int("pack.compression", &pack_compression_level)) {
 +              if (pack_compression_level == -1)
 +                      pack_compression_level = Z_DEFAULT_COMPRESSION;
 +              else if (pack_compression_level < 0 ||
 +                       pack_compression_level > Z_BEST_COMPRESSION)
 +                      git_die_config("pack.compression",
 +                                      "bad pack compression level %d", pack_compression_level);
                pack_compression_seen = 1;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.indexversion")) {
 -              pack_idx_opts.version = git_config_int(k, v);
 +      if (!git_config_get_int("pack.indexversion", &indexversion_value)) {
 +              pack_idx_opts.version = indexversion_value;
                if (pack_idx_opts.version > 2)
 -                      die("bad pack.indexversion=%"PRIu32,
 -                          pack_idx_opts.version);
 -              return 0;
 -      }
 -      if (!strcmp(k, "pack.packsizelimit")) {
 -              max_packsize = git_config_ulong(k, v);
 -              return 0;
 +                      git_die_config("pack.indexversion",
 +                                      "bad pack.indexversion=%"PRIu32, pack_idx_opts.version);
        }
 -      return git_default_config(k, v, cb);
 +      if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
 +              max_packsize = packsizelimit_value;
 +
 +      git_config(git_default_config, NULL);
  }
  
  static const char fast_import_usage[] =
@@@ -3330,21 -3332,18 +3327,21 @@@ static void parse_argv(void
                if (*a != '-' || !strcmp(a, "--"))
                        break;
  
 -              if (parse_one_option(a + 2))
 +              if (!skip_prefix(a, "--", &a))
 +                      die("unknown option %s", a);
 +
 +              if (parse_one_option(a))
                        continue;
  
 -              if (parse_one_feature(a + 2, 0))
 +              if (parse_one_feature(a, 0))
                        continue;
  
 -              if (starts_with(a + 2, "cat-blob-fd=")) {
 -                      option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
 +              if (skip_prefix(a, "cat-blob-fd=", &a)) {
 +                      option_cat_blob_fd(a);
                        continue;
                }
  
 -              die("unknown option %s", a);
 +              die("unknown option --%s", a);
        }
        if (i != global_argc)
                usage(fast_import_usage);
@@@ -3367,7 -3366,7 +3364,7 @@@ int main(int argc, char **argv
  
        setup_git_directory();
        reset_pack_idx_option(&pack_idx_opts);
 -      git_config(git_pack_config, NULL);
 +      git_pack_config();
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
        set_die_routine(die_nicely);
        set_checkpoint_signal();
        while (read_next_command() != EOF) {
 +              const char *v;
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
 -              else if (starts_with(command_buf.buf, "ls "))
 -                      parse_ls(NULL);
 -              else if (starts_with(command_buf.buf, "commit "))
 -                      parse_new_commit();
 -              else if (starts_with(command_buf.buf, "tag "))
 -                      parse_new_tag();
 -              else if (starts_with(command_buf.buf, "reset "))
 -                      parse_reset_branch();
 +              else if (skip_prefix(command_buf.buf, "ls ", &v))
 +                      parse_ls(v, NULL);
 +              else if (skip_prefix(command_buf.buf, "commit ", &v))
 +                      parse_new_commit(v);
 +              else if (skip_prefix(command_buf.buf, "tag ", &v))
 +                      parse_new_tag(v);
 +              else if (skip_prefix(command_buf.buf, "reset ", &v))
 +                      parse_reset_branch(v);
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
                else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
 -              else if (starts_with(command_buf.buf, "feature "))
 -                      parse_feature();
 -              else if (starts_with(command_buf.buf, "option git "))
 -                      parse_option();
 +              else if (skip_prefix(command_buf.buf, "feature ", &v))
 +                      parse_feature(v);
 +              else if (skip_prefix(command_buf.buf, "option git ", &v))
 +                      parse_option(v);
                else if (starts_with(command_buf.buf, "option "))
                        /* ignore non-git options*/;
                else
diff --combined http-push.c
index 0beb7ab67ffc93a6c8fe8517d711945e2c033b7d,8f473b35de30bda806354c90a37c02ffba9c00cc..bfb1c9605b46e8308e91a80c38643ea920cd4d1b
@@@ -365,7 -365,6 +365,6 @@@ static void start_put(struct transfer_r
        hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
  
        /* Set it up */
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        size = git_deflate_bound(&stream, len + hdrlen);
        strbuf_init(&request->buffer.buf, size);
@@@ -719,10 -718,14 +718,10 @@@ static int fetch_indices(void
        return ret;
  }
  
 -static void one_remote_object(const char *hex)
 +static void one_remote_object(const unsigned char *sha1)
  {
 -      unsigned char sha1[20];
        struct object *obj;
  
 -      if (get_sha1_hex(hex, sha1) != 0)
 -              return;
 -
        obj = lookup_object(sha1);
        if (!obj)
                obj = parse_object(sha1);
@@@ -763,13 -766,15 +762,13 @@@ static void handle_new_lock_ctx(struct 
  
        if (tag_closed && ctx->cdata) {
                if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
 -                      lock->owner = xmalloc(strlen(ctx->cdata) + 1);
 -                      strcpy(lock->owner, ctx->cdata);
 +                      lock->owner = xstrdup(ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
 -                      if (starts_with(ctx->cdata, "Second-"))
 -                              lock->timeout =
 -                                      strtol(ctx->cdata + 7, NULL, 10);
 +                      const char *arg;
 +                      if (skip_prefix(ctx->cdata, "Second-", &arg))
 +                              lock->timeout = strtol(arg, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
 -                      lock->token = xmalloc(strlen(ctx->cdata) + 1);
 -                      strcpy(lock->token, ctx->cdata);
 +                      lock->token = xstrdup(ctx->cdata);
  
                        git_SHA1_Init(&sha_ctx);
                        git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
@@@ -850,7 -855,8 +849,7 @@@ static struct remote_lock *lock_remote(
        struct xml_ctx ctx;
        char *escaped;
  
 -      url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 -      sprintf(url, "%s%s", repo->url, path);
 +      url = xstrfmt("%s%s", repo->url, path);
  
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(repo->url) + 1, '/');
@@@ -1013,38 -1019,26 +1012,38 @@@ static void remote_ls(const char *path
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData);
  
 +/* extract hex from sharded "xx/x{40}" filename */
 +static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1)
 +{
 +      char hex[40];
 +
 +      if (strlen(path) != 41)
 +              return -1;
 +
 +      memcpy(hex, path, 2);
 +      path += 2;
 +      path++; /* skip '/' */
 +      memcpy(hex, path, 38);
 +
 +      return get_sha1_hex(hex, sha1);
 +}
 +
  static void process_ls_object(struct remote_ls_ctx *ls)
  {
        unsigned int *parent = (unsigned int *)ls->userData;
 -      char *path = ls->dentry_name;
 -      char *obj_hex;
 +      const char *path = ls->dentry_name;
 +      unsigned char sha1[20];
  
        if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
                remote_dir_exists[*parent] = 1;
                return;
        }
  
 -      if (strlen(path) != 49)
 +      if (!skip_prefix(path, "objects/", &path) ||
 +          get_sha1_hex_from_objpath(path, sha1))
                return;
 -      path += 8;
 -      obj_hex = xmalloc(strlen(path));
 -      /* NB: path is not null-terminated, can not use strlcpy here */
 -      memcpy(obj_hex, path, 2);
 -      strcpy(obj_hex + 2, path + 3);
 -      one_remote_object(obj_hex);
 -      free(obj_hex);
 +
 +      one_remote_object(sha1);
  }
  
  static void process_ls_ref(struct remote_ls_ctx *ls)
@@@ -1122,7 -1116,7 +1121,7 @@@ static void remote_ls(const char *path
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
  {
 -      char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 +      char *url = xstrfmt("%s%s", repo->url, path);
        struct active_request_slot *slot;
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
        ls.userData = userData;
        ls.userFunc = userFunc;
  
 -      sprintf(url, "%s%s", repo->url, path);
 -
        strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
  
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
@@@ -1539,9 -1535,10 +1538,9 @@@ static void update_remote_info_refs(str
  
  static int remote_exists(const char *path)
  {
 -      char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 +      char *url = xstrfmt("%s%s", repo->url, path);
        int ret;
  
 -      sprintf(url, "%s%s", repo->url, path);
  
        switch (http_get_strbuf(url, NULL, NULL)) {
        case HTTP_OK:
  
  static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
  {
 -      char *url;
 +      char *url = xstrfmt("%s%s", repo->url, path);
        struct strbuf buffer = STRBUF_INIT;
 -
 -      url = xmalloc(strlen(repo->url) + strlen(path) + 1);
 -      sprintf(url, "%s%s", repo->url, path);
 +      const char *name;
  
        if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
                die("Couldn't get %s for remote symref\n%s", url,
        if (buffer.len == 0)
                return;
  
 +      /* Cut off trailing newline. */
 +      strbuf_rtrim(&buffer);
 +
        /* If it's a symref, set the refname; otherwise try for a sha1 */
 -      if (starts_with((char *)buffer.buf, "ref: ")) {
 -              *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
 +      if (skip_prefix(buffer.buf, "ref: ", &name)) {
 +              *symref = xmemdupz(name, buffer.len - (name - buffer.buf));
        } else {
                get_sha1_hex(buffer.buf, sha1);
        }
@@@ -1676,7 -1672,8 +1675,7 @@@ static int delete_remote_branch(const c
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
        if (dry_run)
                return 0;
 -      url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
 -      sprintf(url, "%s%s", repo->url, remote_ref->name);
 +      url = xstrfmt("%s%s", repo->url, remote_ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_setup_http_get(slot->curl, url, DAV_DELETE);
diff --combined remote-curl.c
index deb4bfe684512ea48fbc0d2663270e44722db26b,13c639c632eb96ed559aee97239b387eccc5f9e3..af7b6786dc091035e3216c710dbc0ebc3c234a8a
@@@ -25,8 -25,7 +25,8 @@@ struct options 
                update_shallow : 1,
                followtags : 1,
                dry_run : 1,
 -              thin : 1;
 +              thin : 1,
 +              push_cert : 1;
  };
  static struct options options;
  static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@@ -107,14 -106,6 +107,14 @@@ static int set_option(const char *name
                else
                        return -1;
                return 0;
 +      } else if (!strcmp(name, "pushcert")) {
 +              if (!strcmp(value, "true"))
 +                      options.push_cert = 1;
 +              else if (!strcmp(value, "false"))
 +                      options.push_cert = 0;
 +              else
 +                      return -1;
 +              return 0;
        } else {
                return 1 /* unsupported */;
        }
@@@ -203,19 -194,19 +203,19 @@@ static void free_discovery(struct disco
        }
  }
  
 -static int show_http_message(struct strbuf *type, struct strbuf *msg)
 +static int show_http_message(struct strbuf *type, struct strbuf *charset,
 +                           struct strbuf *msg)
  {
        const char *p, *eol;
  
        /*
         * We only show text/plain parts, as other types are likely
         * to be ugly to look at on the user's terminal.
 -       *
 -       * TODO should handle "; charset=XXX", and re-encode into
 -       * logoutputencoding
         */
 -      if (strcasecmp(type->buf, "text/plain"))
 +      if (strcmp(type->buf, "text/plain"))
                return -1;
 +      if (charset->len)
 +              strbuf_reencode(msg, charset->buf, get_log_output_encoding());
  
        strbuf_trim(msg);
        if (!msg->len)
        return 0;
  }
  
 -static struct discoverydiscover_refs(const char *service, int for_push)
 +static struct discovery *discover_refs(const char *service, int for_push)
  {
        struct strbuf exp = STRBUF_INIT;
        struct strbuf type = STRBUF_INIT;
 +      struct strbuf charset = STRBUF_INIT;
        struct strbuf buffer = STRBUF_INIT;
        struct strbuf refs_url = STRBUF_INIT;
        struct strbuf effective_url = STRBUF_INIT;
  
        memset(&options, 0, sizeof(options));
        options.content_type = &type;
 +      options.charset = &charset;
        options.effective_url = &effective_url;
        options.base_url = &url;
        options.no_cache = 1;
        case HTTP_OK:
                break;
        case HTTP_MISSING_TARGET:
 -              show_http_message(&type, &buffer);
 +              show_http_message(&type, &charset, &buffer);
                die("repository '%s' not found", url.buf);
        case HTTP_NOAUTH:
 -              show_http_message(&type, &buffer);
 +              show_http_message(&type, &charset, &buffer);
                die("Authentication failed for '%s'", url.buf);
        default:
 -              show_http_message(&type, &buffer);
 +              show_http_message(&type, &charset, &buffer);
                die("unable to access '%s': %s", url.buf, curl_errorstr);
        }
  
        strbuf_release(&refs_url);
        strbuf_release(&exp);
        strbuf_release(&type);
 +      strbuf_release(&charset);
        strbuf_release(&effective_url);
        strbuf_release(&buffer);
        last_discovery = last;
@@@ -408,7 -396,7 +408,7 @@@ static curlioerr rpc_ioctl(CURL *handle
                        rpc->pos = 0;
                        return CURLIOE_OK;
                }
 -              fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
 +              error("unable to rewind rpc post data - try increasing http.postBuffer");
                return CURLIOE_FAILRESTART;
  
        default:
@@@ -567,7 -555,6 +567,6 @@@ retry
                git_zstream stream;
                int ret;
  
-               memset(&stream, 0, sizeof(stream));
                git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
                gzip_size = git_deflate_bound(&stream, rpc->len);
                gzip_body = xmalloc(gzip_size);
@@@ -632,9 -619,10 +631,9 @@@ static int rpc_service(struct rpc_stat
        const char *svc = rpc->service_name;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf *preamble = rpc->stdin_preamble;
 -      struct child_process client;
 +      struct child_process client = CHILD_PROCESS_INIT;
        int err = 0;
  
 -      memset(&client, 0, sizeof(client));
        client.in = -1;
        client.out = -1;
        client.git_cmd = 1;
@@@ -717,7 -705,7 +716,7 @@@ static int fetch_dumb(int nr_heads, str
                free(targets[i]);
        free(targets);
  
 -      return ret ? error("Fetch failed.") : 0;
 +      return ret ? error("fetch failed.") : 0;
  }
  
  static int fetch_git(struct discovery *heads,
  
        for (i = 0; i < nr_heads; i++) {
                struct ref *ref = to_fetch[i];
 -              if (!ref->name || !*ref->name)
 +              if (!*ref->name)
                        die("cannot fetch by sha1 over smart http");
                packet_buf_write(&preamble, "%s %s\n",
                                 sha1_to_hex(ref->old_sha1), ref->name);
@@@ -799,9 -787,9 +798,9 @@@ static void parse_fetch(struct strbuf *
        int alloc_heads = 0, nr_heads = 0;
  
        do {
 -              if (starts_with(buf->buf, "fetch ")) {
 -                      char *p = buf->buf + strlen("fetch ");
 -                      char *name;
 +              const char *p;
 +              if (skip_prefix(buf->buf, "fetch ", &p)) {
 +                      const char *name;
                        struct ref *ref;
                        unsigned char old_sha1[20];
  
@@@ -871,7 -859,6 +870,7 @@@ static int push_git(struct discovery *h
        int i, err;
        struct argv_array args;
        struct string_list_item *cas_option;
 +      struct strbuf preamble = STRBUF_INIT;
  
        argv_array_init(&args);
        argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
                argv_array_push(&args, "--thin");
        if (options.dry_run)
                argv_array_push(&args, "--dry-run");
 +      if (options.push_cert)
 +              argv_array_push(&args, "--signed");
        if (options.verbosity == 0)
                argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
        for_each_string_list_item(cas_option, &cas_options)
                argv_array_push(&args, cas_option->string);
        argv_array_push(&args, url.buf);
 +
 +      argv_array_push(&args, "--stdin");
        for (i = 0; i < nr_spec; i++)
 -              argv_array_push(&args, specs[i]);
 +              packet_buf_write(&preamble, "%s\n", specs[i]);
 +      packet_buf_flush(&preamble);
  
        memset(&rpc, 0, sizeof(rpc));
        rpc.service_name = "git-receive-pack",
        rpc.argv = args.argv;
 +      rpc.stdin_preamble = &preamble;
  
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
                write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
 +      strbuf_release(&preamble);
        argv_array_clear(&args);
        return err;
  }
@@@ -962,12 -942,10 +961,12 @@@ int main(int argc, const char **argv
        struct strbuf buf = STRBUF_INIT;
        int nongit;
  
 +      git_setup_gettext();
 +
        git_extract_argv0_path(argv[0]);
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
 -              fprintf(stderr, "Remote needed\n");
 +              error("remote-curl: usage: git remote-curl <remote> [<url>]");
                return 1;
        }
  
        http_init(remote, url.buf, 0);
  
        do {
 +              const char *arg;
 +
                if (strbuf_getline(&buf, stdin, '\n') == EOF) {
                        if (ferror(stdin))
 -                              fprintf(stderr, "Error reading command stream\n");
 -                      else
 -                              fprintf(stderr, "Unexpected end of command stream\n");
 +                              error("remote-curl: error reading command stream from git");
                        return 1;
                }
                if (buf.len == 0)
                        break;
                if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
 -                              die("Fetch attempted without a local repo");
 +                              die("remote-curl: fetch attempted without a local repo");
                        parse_fetch(&buf);
  
                } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
                } else if (starts_with(buf.buf, "push ")) {
                        parse_push(&buf);
  
 -              } else if (starts_with(buf.buf, "option ")) {
 -                      char *name = buf.buf + strlen("option ");
 -                      char *value = strchr(name, ' ');
 +              } else if (skip_prefix(buf.buf, "option ", &arg)) {
 +                      char *value = strchr(arg, ' ');
                        int result;
  
                        if (value)
                        else
                                value = "true";
  
 -                      result = set_option(name, value);
 +                      result = set_option(arg, value);
                        if (!result)
                                printf("ok\n");
                        else if (result < 0)
                        printf("\n");
                        fflush(stdout);
                } else {
 -                      fprintf(stderr, "Unknown command '%s'\n", buf.buf);
 +                      error("remote-curl: unknown command '%s' from git", buf.buf);
                        return 1;
                }
                strbuf_reset(&buf);
diff --combined sha1_file.c
index 69a60ec88bad190f885cd89a93573236a88ddbd9,634c4ac3028f7dceb63fee9ba560f17b57cab55a..88f06bac926008dd40b6a53e0696274438278cfc
@@@ -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"
@@@ -37,6 -36,9 +37,6 @@@ static inline uintmax_t sz_fmt(size_t s
  
  const unsigned char null_sha1[20];
  
 -static const char *no_log_pack_access = "no_log_pack_access";
 -static const char *log_pack_access;
 -
  /*
   * 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
@@@ -266,9 -268,9 +266,9 @@@ static struct alternate_object_databas
   * SHA1, an extra slash for the first level indirection, and the
   * terminating NUL.
   */
 -static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
 +static int link_alt_odb_entry(const char *entry, const char *relative_base,
 +      int depth, const char *normalized_objdir)
  {
 -      const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
        int pfxlen, entlen;
                        return -1;
                }
        }
 -      if (!strcmp(ent->base, objdir)) {
 +      if (!strcmp_icase(ent->base, normalized_objdir)) {
                free(ent);
                return -1;
        }
@@@ -343,7 -345,6 +343,7 @@@ static void link_alt_odb_entries(const 
        struct string_list entries = STRING_LIST_INIT_NODUP;
        char *alt_copy;
        int i;
 +      struct strbuf objdirbuf = STRBUF_INIT;
  
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                return;
        }
  
 +      strbuf_add_absolute_path(&objdirbuf, get_object_directory());
 +      normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
 +
        alt_copy = xmemdupz(alt, len);
        string_list_split_in_place(&entries, alt_copy, sep, -1);
        for (i = 0; i < entries.nr; i++) {
                        error("%s: ignoring relative alternate object store %s",
                                        relative_base, entry);
                } else {
 -                      link_alt_odb_entry(entry, relative_base, depth);
 +                      link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
                }
        }
        string_list_clear(&entries, 0);
        free(alt_copy);
 +      strbuf_release(&objdirbuf);
  }
  
  void read_info_alternates(const char * relative_base, int depth)
@@@ -413,18 -410,14 +413,18 @@@ void add_to_alternates_file(const char 
                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 -661,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 -1149,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;
  
  static void prepare_packed_git_one(char *objdir, int local)
  {
 -      /* Ensure that this buffer is large enough so that we can
 -         append "/pack/" without clobbering the stack even if
 -         strlen(objdir) were PATH_MAX.  */
 -      char path[PATH_MAX + 1 + 4 + 1 + 1];
 -      int len;
 +      struct strbuf path = STRBUF_INIT;
 +      size_t dirnamelen;
        DIR *dir;
        struct dirent *de;
        struct string_list garbage = STRING_LIST_INIT_DUP;
  
 -      sprintf(path, "%s/pack", objdir);
 -      len = strlen(path);
 -      dir = opendir(path);
 +      strbuf_addstr(&path, objdir);
 +      strbuf_addstr(&path, "/pack");
 +      dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
 -                            path, strerror(errno));
 +                            path.buf, strerror(errno));
 +              strbuf_release(&path);
                return;
        }
 -      path[len++] = '/';
 +      strbuf_addch(&path, '/');
 +      dirnamelen = path.len;
        while ((de = readdir(dir)) != NULL) {
 -              int namelen = strlen(de->d_name);
                struct packed_git *p;
 -
 -              if (len + namelen + 1 > sizeof(path)) {
 -                      if (report_garbage) {
 -                              struct strbuf sb = STRBUF_INIT;
 -                              strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
 -                              report_garbage("path too long", sb.buf);
 -                              strbuf_release(&sb);
 -                      }
 -                      continue;
 -              }
 +              size_t base_len;
  
                if (is_dot_or_dotdot(de->d_name))
                        continue;
  
 -              strcpy(path + len, de->d_name);
 +              strbuf_setlen(&path, dirnamelen);
 +              strbuf_addstr(&path, de->d_name);
  
 -              if (has_extension(de->d_name, ".idx")) {
 +              base_len = path.len;
 +              if (strip_suffix_mem(path.buf, &base_len, ".idx")) {
                        /* Don't reopen a pack we already have. */
                        for (p = packed_git; p; p = p->next) {
 -                              if (!memcmp(path, p->pack_name, len + namelen - 4))
 +                              size_t len;
 +                              if (strip_suffix(p->pack_name, ".pack", &len) &&
 +                                  len == base_len &&
 +                                  !memcmp(p->pack_name, path.buf, len))
                                        break;
                        }
                        if (p == NULL &&
                             * See if it really is a valid .idx file with
                             * corresponding .pack file that we can map.
                             */
 -                          (p = add_packed_git(path, len + namelen, local)) != NULL)
 +                          (p = add_packed_git(path.buf, path.len, local)) != NULL)
                                install_packed_git(p);
                }
  
                if (!report_garbage)
                        continue;
  
 -              if (has_extension(de->d_name, ".idx") ||
 -                  has_extension(de->d_name, ".pack") ||
 -                  has_extension(de->d_name, ".bitmap") ||
 -                  has_extension(de->d_name, ".keep"))
 -                      string_list_append(&garbage, path);
 +              if (ends_with(de->d_name, ".idx") ||
 +                  ends_with(de->d_name, ".pack") ||
 +                  ends_with(de->d_name, ".bitmap") ||
 +                  ends_with(de->d_name, ".keep"))
 +                      string_list_append(&garbage, path.buf);
                else
 -                      report_garbage("garbage found", path);
 +                      report_garbage("garbage found", path.buf);
        }
        closedir(dir);
        report_pack_garbage(&garbage);
        string_list_clear(&garbage, 0);
 +      strbuf_release(&path);
  }
  
  static int sort_pack(const void *a_, const void *b_)
@@@ -1970,9 -1926,7 +1970,9 @@@ static void *unpack_compressed_entry(st
        git_zstream stream;
        unsigned char *buffer, *in;
  
 -      buffer = xmallocz(size);
 +      buffer = xmallocz_gently(size);
 +      if (!buffer)
 +              return NULL;
        memset(&stream, 0, sizeof(stream));
        stream.next_out = buffer;
        stream.avail_out = size + 1;
@@@ -2132,9 -2086,27 +2132,9 @@@ static void *read_object(const unsigne
  
  static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
  {
 -      static FILE *log_file;
 -
 -      if (!log_pack_access)
 -              log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
 -      if (!log_pack_access)
 -              log_pack_access = no_log_pack_access;
 -      if (log_pack_access == no_log_pack_access)
 -              return;
 -
 -      if (!log_file) {
 -              log_file = fopen(log_pack_access, "w");
 -              if (!log_file) {
 -                      error("cannot open pack access log '%s' for writing: %s",
 -                            log_pack_access, strerror(errno));
 -                      log_pack_access = no_log_pack_access;
 -                      return;
 -              }
 -      }
 -      fprintf(log_file, "%s %"PRIuMAX"\n",
 -              p->pack_name, (uintmax_t)obj_offset);
 -      fflush(log_file);
 +      static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
 +      trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
 +                       p->pack_name, (uintmax_t)obj_offset);
  }
  
  int do_check_packed_object_crc;
@@@ -2159,7 -2131,8 +2159,7 @@@ void *unpack_entry(struct packed_git *p
        int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
        int base_from_cache = 0;
  
 -      if (log_pack_access != no_log_pack_access)
 -              write_pack_access_log(p, obj_offset);
 +      write_pack_access_log(p, obj_offset);
  
        /* PHASE 1: drill down to the innermost base object */
        for (;;) {
@@@ -2943,7 -2916,6 +2943,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);
  }
  
 +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;
 +      return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
 +}
 +
  int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
  {
        unsigned char sha1[20];
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        if (returnsha1)
                hashcpy(returnsha1, sha1);
 -      if (has_sha1_file(sha1))
 +      if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
  }
@@@ -3134,29 -3095,6 +3133,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)
  {
@@@ -3222,22 -3160,15 +3221,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;
  }
@@@ -3302,167 -3233,3 +3301,167 @@@ 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)
 +{
 +      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;
 +
 +      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)
 +{
 +      struct packed_git *p;
 +      int r = 0;
 +
 +      prepare_packed_git();
 +      for (p = packed_git; p; p = p->next) {
 +              r = for_each_object_in_pack(p, cb, data);
 +              if (r)
 +                      break;
 +      }
 +      return r;
 +}