Merge branch 'jk/strip-suffix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:25:59 +0000 (11:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Jul 2014 18:26:00 +0000 (11:26 -0700)
* jk/strip-suffix:
prepare_packed_git_one: refactor duplicate-pack check
verify-pack: use strbuf_strip_suffix
strbuf: implement strbuf_strip_suffix
index-pack: use strip_suffix to avoid magic numbers
use strip_suffix instead of ends_with in simple cases
replace has_extension with ends_with
implement ends_with via strip_suffix
add strip_suffix function
sha1_file: replace PATH_MAX buffer with strbuf in prepare_packed_git_one()

1  2 
builtin/index-pack.c
builtin/remote.c
builtin/repack.c
git-compat-util.h
help.c
refs.c
sha1_file.c
strbuf.c
strbuf.h
diff --combined builtin/index-pack.c
index 8b3bd29dbcf22668040c53fffb6e2414ca090f5d,d4b77fd1285a4305341a358386b05434fab7e5e9..fc40411892f13da7e739bc4801bbace3021c0e2b
@@@ -786,8 -786,7 +786,8 @@@ static void sha1_object(const void *dat
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
 -                              commit->buffer = NULL;
 +                              if (detach_commit_buffer(commit, NULL) != data)
 +                                      die("BUG: parse_object_buffer transmogrified our buffer");
                        }
                        obj->flags |= FLAG_CHECKED;
                }
@@@ -1506,7 -1505,8 +1506,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/remote.c
index a8efe3da4d7dad3b17582c91671f0f475d54a9ac,0a6f3ef04060a918c87f99ccc4c661f2e06541fd..8e1dc39162681503505c9c3897e046514ce2e6a8
@@@ -250,7 -250,9 +250,7 @@@ static struct string_list branch_list
  
  static const char *abbrev_ref(const char *name, const char *prefix)
  {
 -      const char *abbrev = skip_prefix(name, prefix);
 -      if (abbrev)
 -              return abbrev;
 +      skip_prefix(name, prefix, &name);
        return name;
  }
  #define abbrev_branch(name) abbrev_ref((name), "refs/heads/")
@@@ -263,16 -265,17 +263,17 @@@ static int config_read_branches(const c
                struct string_list_item *item;
                struct branch_info *info;
                enum { REMOTE, MERGE, REBASE } type;
+               size_t key_len;
  
                key += 7;
-               if (ends_with(key, ".remote")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               if (strip_suffix(key, ".remote", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REMOTE;
-               } else if (ends_with(key, ".merge")) {
-                       name = xstrndup(key, strlen(key) - 6);
+               } else if (strip_suffix(key, ".merge", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = MERGE;
-               } else if (ends_with(key, ".rebase")) {
-                       name = xstrndup(key, strlen(key) - 7);
+               } else if (strip_suffix(key, ".rebase", &key_len)) {
+                       name = xmemdupz(key, key_len);
                        type = REBASE;
                } else
                        return 0;
                item = string_list_insert(&branch_list, name);
  
                if (!item->util)
 -                      item->util = xcalloc(sizeof(struct branch_info), 1);
 +                      item->util = xcalloc(1, sizeof(struct branch_info));
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
@@@ -396,7 -399,7 +397,7 @@@ static int get_push_ref_states(const st
  
                item = string_list_append(&states->push,
                                          abbrev_branch(ref->peer_ref->name));
 -              item->util = xcalloc(sizeof(struct push_info), 1);
 +              item->util = xcalloc(1, sizeof(struct push_info));
                info = item->util;
                info->forced = ref->force;
                info->dest = xstrdup(abbrev_branch(ref->name));
@@@ -431,7 -434,7 +432,7 @@@ static int get_push_ref_states_noquery(
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
                item = string_list_append(&states->push, _("(matching)"));
 -              info = item->util = xcalloc(sizeof(struct push_info), 1);
 +              info = item->util = xcalloc(1, sizeof(struct push_info));
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        }
                else
                        item = string_list_append(&states->push, _("(delete)"));
  
 -              info = item->util = xcalloc(sizeof(struct push_info), 1);
 +              info = item->util = xcalloc(1, sizeof(struct push_info));
                info->forced = spec->force;
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(spec->dst ? spec->dst : item->string);
diff --combined builtin/repack.c
index ff2216a7aaefa3b38385301e4e0d8bb8b94e4ea0,52f22ca3fb7923b16237e8430bda0e1426c8bff2..a77e743b94036b2d856e6b3f74999170e39b5f17
@@@ -10,7 -10,6 +10,7 @@@
  
  static int delta_base_offset = 1;
  static int pack_kept_objects = -1;
 +static int write_bitmaps;
  static char *packdir, *packtmp;
  
  static const char *const git_repack_usage[] = {
@@@ -28,11 -27,6 +28,11 @@@ static int repack_config(const char *va
                pack_kept_objects = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "repack.writebitmaps") ||
 +          !strcmp(var, "pack.writebitmaps")) {
 +              write_bitmaps = git_config_bool(var, value);
 +              return 0;
 +      }
        return git_default_config(var, value, cb);
  }
  
@@@ -83,16 -77,15 +83,15 @@@ static void get_non_kept_pack_filenames
        DIR *dir;
        struct dirent *e;
        char *fname;
-       size_t len;
  
        if (!(dir = opendir(packdir)))
                return;
  
        while ((e = readdir(dir)) != NULL) {
-               if (!ends_with(e->d_name, ".pack"))
+               size_t len;
+               if (!strip_suffix(e->d_name, ".pack", &len))
                        continue;
  
-               len = strlen(e->d_name) - strlen(".pack");
                fname = xmemdupz(e->d_name, len);
  
                if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
@@@ -155,6 -148,7 +154,6 @@@ int cmd_repack(int argc, const char **a
        int no_update_server_info = 0;
        int quiet = 0;
        int local = 0;
 -      int write_bitmap = -1;
  
        struct option builtin_repack_options[] = {
                OPT_BIT('a', NULL, &pack_everything,
                OPT__QUIET(&quiet, N_("be quiet")),
                OPT_BOOL('l', "local", &local,
                                N_("pass --local to git-pack-objects")),
 -              OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
 +              OPT_BOOL('b', "write-bitmap-index", &write_bitmaps,
                                N_("write bitmap index")),
                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
                                N_("with -A, do not loosen objects older than this")),
                                git_repack_usage, 0);
  
        if (pack_kept_objects < 0)
 -              pack_kept_objects = write_bitmap;
 +              pack_kept_objects = write_bitmaps;
  
        packdir = mkpathdup("%s/pack", get_object_directory());
        packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
                argv_array_pushf(&cmd_args, "--no-reuse-delta");
        if (no_reuse_object)
                argv_array_pushf(&cmd_args, "--no-reuse-object");
 -      if (write_bitmap >= 0)
 -              argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
 -                               write_bitmap ? "" : "no-");
 +      if (write_bitmaps)
 +              argv_array_push(&cmd_args, "--write-bitmap-index");
  
        if (pack_everything & ALL_INTO_ONE) {
                get_non_kept_pack_filenames(&existing_packs);
diff --combined git-compat-util.h
index 9de31807108322aac327f66822d4929974d7ffd5,47a49c355dd579fdb2d8b48d3d89ea9e9c058085..0b53c9a4af3c7e1d5611065b35ef38f3f1f5e5be
@@@ -267,10 -267,6 +267,10 @@@ extern char *gitbasename(char *)
  #define has_dos_drive_prefix(path) 0
  #endif
  
 +#ifndef offset_1st_component
 +#define offset_1st_component(path) (is_dir_sep((path)[0]))
 +#endif
 +
  #ifndef is_dir_sep
  #define is_dir_sep(c) ((c) == '/')
  #endif
@@@ -334,12 -330,8 +334,12 @@@ extern void warning(const char *err, ..
   * trying to help gcc, anyway, it's OK; other compilers will fall back to
   * using the function as usual.
   */
 -#if defined(__GNUC__) && ! defined(__clang__)
 -#define error(...) (error(__VA_ARGS__), -1)
 +#if defined(__GNUC__)
 +static inline int const_error(void)
 +{
 +      return -1;
 +}
 +#define error(...) (error(__VA_ARGS__), const_error())
  #endif
  
  extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
@@@ -347,36 -339,49 +347,68 @@@ extern void set_error_routine(void (*ro
  extern void set_die_is_recursing_routine(int (*routine)(void));
  
  extern int starts_with(const char *str, const char *prefix);
- extern int ends_with(const char *str, const char *suffix);
  
 -static inline const char *skip_prefix(const char *str, const char *prefix)
 +/*
 + * If the string "str" begins with the string found in "prefix", return 1.
 + * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
 + * the string right after the prefix).
 + *
 + * Otherwise, return 0 and leave "out" untouched.
 + *
 + * Examples:
 + *
 + *   [extract branch name, fail if not a branch]
 + *   if (!skip_prefix(ref, "refs/heads/", &branch)
 + *    return -1;
 + *
 + *   [skip prefix if present, otherwise use whole string]
 + *   skip_prefix(name, "refs/heads/", &name);
 + */
 +static inline int skip_prefix(const char *str, const char *prefix,
 +                            const char **out)
  {
        do {
 -              if (!*prefix)
 -                      return str;
 +              if (!*prefix) {
 +                      *out = str;
 +                      return 1;
 +              }
        } while (*str++ == *prefix++);
 -      return NULL;
 +      return 0;
  }
  
+ /*
+  * If buf ends with suffix, return 1 and subtract the length of the suffix
+  * from *len. Otherwise, return 0 and leave *len untouched.
+  */
+ static inline int strip_suffix_mem(const char *buf, size_t *len,
+                                  const char *suffix)
+ {
+       size_t suflen = strlen(suffix);
+       if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
+               return 0;
+       *len -= suflen;
+       return 1;
+ }
+ /*
+  * If str ends with suffix, return 1 and set *len to the size of the string
+  * without the suffix. Otherwise, return 0 and set *len to the size of the
+  * string.
+  *
+  * Note that we do _not_ NUL-terminate str to the new length.
+  */
+ static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+ {
+       *len = strlen(str);
+       return strip_suffix_mem(str, len, suffix);
+ }
+ static inline int ends_with(const char *str, const char *suffix)
+ {
+       size_t len;
+       return strip_suffix(str, suffix, &len);
+ }
  #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
  
  #ifndef PROT_READ
@@@ -548,14 -553,6 +580,14 @@@ extern void release_pack_memory(size_t)
  typedef void (*try_to_free_t)(size_t);
  extern try_to_free_t set_try_to_free_routine(try_to_free_t);
  
 +#ifdef HAVE_ALLOCA_H
 +# include <alloca.h>
 +# define xalloca(size)      (alloca(size))
 +# define xalloca_free(p)    do {} while (0)
 +#else
 +# define xalloca(size)      (xmalloc(size))
 +# define xalloca_free(p)    (free(p))
 +#endif
  extern char *xstrdup(const char *str);
  extern void *xmalloc(size_t size);
  extern void *xmallocz(size_t size);
@@@ -581,13 -578,6 +613,6 @@@ static inline size_t xsize_t(off_t len
        return (size_t)len;
  }
  
- static inline int has_extension(const char *filename, const char *ext)
- {
-       size_t len = strlen(filename);
-       size_t extlen = strlen(ext);
-       return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
- }
  /* in ctype.c, for kwset users */
  extern const char tolower_trans_tbl[256];
  
@@@ -704,17 -694,6 +729,17 @@@ void git_qsort(void *base, size_t nmemb
  #endif
  #endif
  
 +#if defined(__GNUC__) && defined(__x86_64__)
 +#include <emmintrin.h>
 +/*
 + * This is the system memory page size; it's used so that we can read
 + * outside the bounds of an allocation without segfaulting.
 + */
 +#ifndef PAGE_SIZE
 +#define PAGE_SIZE 4096
 +#endif
 +#endif
 +
  #ifdef UNRELIABLE_FSTAT
  #define fstat_is_reliable() 0
  #else
diff --combined help.c
index f31f29ac421e224a59726fc1f2dfc16d92faad0f,97567c452364838ea720fc8f61d2e9ab96304613..7af65e205ecdf1a01dce009cbf0ada15de68c844
--- 1/help.c
--- 2/help.c
+++ b/help.c
@@@ -129,6 -129,7 +129,6 @@@ static void list_commands_in_dir(struc
                                         const char *path,
                                         const char *prefix)
  {
 -      int prefix_len;
        DIR *dir = opendir(path);
        struct dirent *de;
        struct strbuf buf = STRBUF_INIT;
                return;
        if (!prefix)
                prefix = "git-";
 -      prefix_len = strlen(prefix);
  
        strbuf_addf(&buf, "%s/", path);
        len = buf.len;
  
        while ((de = readdir(dir)) != NULL) {
-               int entlen;
 +              const char *ent;
+               size_t entlen;
  
 -              if (!starts_with(de->d_name, prefix))
 +              if (!skip_prefix(de->d_name, prefix, &ent))
                        continue;
  
                strbuf_setlen(&buf, len);
                if (!is_executable(buf.buf))
                        continue;
  
 -              entlen = strlen(de->d_name) - prefix_len;
 -              strip_suffix(de->d_name, ".exe", &entlen);
 +              entlen = strlen(ent);
-               if (has_extension(ent, ".exe"))
-                       entlen -= 4;
++              strip_suffix(ent, ".exe", &entlen);
  
 -              add_cmdname(cmds, de->d_name + prefix_len, entlen);
 +              add_cmdname(cmds, ent, entlen);
        }
        closedir(dir);
        strbuf_release(&buf);
@@@ -250,13 -250,11 +249,13 @@@ static struct cmdnames aliases
  
  static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
  {
 +      const char *p;
 +
        if (!strcmp(var, "help.autocorrect"))
                autocorrect = git_config_int(var,value);
        /* Also use aliases for command lookup */
 -      if (starts_with(var, "alias."))
 -              add_cmdname(&aliases, var + 6, strlen(var + 6));
 +      if (skip_prefix(var, "alias.", &p))
 +              add_cmdname(&aliases, p, strlen(p));
  
        return git_default_config(var, value, cb);
  }
@@@ -413,12 -411,11 +412,12 @@@ static int append_similar_ref(const cha
  {
        struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
        char *branch = strrchr(refname, '/') + 1;
 +      const char *remote;
 +
        /* A remote branch of the same name is deemed similar */
 -      if (starts_with(refname, "refs/remotes/") &&
 +      if (skip_prefix(refname, "refs/remotes/", &remote) &&
            !strcmp(branch, cb->base_ref))
 -              string_list_append(cb->similar_refs,
 -                                 refname + strlen("refs/remotes/"));
 +              string_list_append(cb->similar_refs, remote);
        return 0;
  }
  
diff --combined refs.c
index 82e4842a36ef803d6406f852f2415b3b77f477f7,02ce29c575587895ab3722cb8ede79d7c3ff4cf0..061d233e4b6404c57ff8d62715758b900d02e051
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -6,71 -6,51 +6,71 @@@
  #include "string-list.h"
  
  /*
 - * Make sure "ref" is something reasonable to have under ".git/refs/";
 - * We do not like it if:
 + * How to handle various characters in refnames:
 + * This table is used by both the SIMD and non-SIMD code.  It has
 + * some cases that are only useful for the SIMD; these are handled
 + * equivalently to the listed disposition in the non-SIMD code.
 + * 0: An acceptable character for refs
 + * 1: @, look for a following { to reject @{ in refs (SIMD or = 0)
 + * 2: \0: End-of-component and string
 + * 3: /: End-of-component (SIMD or = 2)
 + * 4: ., look for a preceding . to reject .. in refs
 + * 5: {, look for a preceding @ to reject @{ in refs
 + * 6: *, usually a bad character except, once as a wildcard (SIMD or = 7)
 + * 7: A bad character except * (see check_refname_component below)
 + */
 +static unsigned char refname_disposition[256] = {
 +      2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 +      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
 +      7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 4, 3,
 +      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7,
 +      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 7, 0,
 +      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 +      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
 +};
 +
 +/*
 + * Try to read one refname component from the front of refname.
 + * Return the length of the component found, or -1 if the component is
 + * not legal.  It is legal if it is something reasonable to have under
 + * ".git/refs/"; We do not like it if:
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
   * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 - * - it ends with a "/".
 - * - it ends with ".lock"
 + * - it has pattern-matching notation "*", "?", "[", anywhere, or
 + * - it ends with a "/", or
 + * - it ends with ".lock", or
   * - it contains a "\" (backslash)
   */
 -
 -/* Return true iff ch is not allowed in reference names. */
 -static inline int bad_ref_char(int ch)
 -{
 -      if (((unsigned) ch) <= ' ' || ch == 0x7f ||
 -          ch == '~' || ch == '^' || ch == ':' || ch == '\\')
 -              return 1;
 -      /* 2.13 Pattern Matching Notation */
 -      if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
 -              return 1;
 -      return 0;
 -}
 -
 -/*
 - * Try to read one refname component from the front of refname.  Return
 - * the length of the component found, or -1 if the component is not
 - * legal.
 - */
  static int check_refname_component(const char *refname, int flags)
  {
        const char *cp;
        char last = '\0';
  
        for (cp = refname; ; cp++) {
 -              char ch = *cp;
 -              if (ch == '\0' || ch == '/')
 +              int ch = *cp & 255;
 +              unsigned char disp = refname_disposition[ch];
 +              switch (disp) {
 +              case 2: /* fall-through */
 +              case 3:
 +                      goto out;
 +              case 4:
 +                      if (last == '.')
 +                              return -1; /* Refname contains "..". */
                        break;
 -              if (bad_ref_char(ch))
 -                      return -1; /* Illegal character in refname. */
 -              if (last == '.' && ch == '.')
 -                      return -1; /* Refname contains "..". */
 -              if (last == '@' && ch == '{')
 -                      return -1; /* Refname contains "@{". */
 +              case 5:
 +                      if (last == '@')
 +                              return -1; /* Refname contains "@{". */
 +                      break;
 +              case 6: /* fall-through */
 +              case 7:
 +                      return -1;
 +              }
                last = ch;
        }
 +out:
        if (cp == refname)
                return 0; /* Component has zero length. */
        if (refname[0] == '.') {
@@@ -88,7 -68,7 +88,7 @@@
        return cp - refname;
  }
  
 -int check_refname_format(const char *refname, int flags)
 +static int check_refname_format_bytewise(const char *refname, int flags)
  {
        int component_len, component_count = 0;
  
        return 0;
  }
  
 +#if defined(__GNUC__) && defined(__x86_64__)
 +#define SSE_VECTOR_BYTES 16
 +
 +/* Vectorized version of check_refname_format. */
 +int check_refname_format(const char *refname, int flags)
 +{
 +      const char *cp = refname;
 +
 +      const __m128i dot = _mm_set1_epi8('.');
 +      const __m128i at = _mm_set1_epi8('@');
 +      const __m128i curly = _mm_set1_epi8('{');
 +      const __m128i slash = _mm_set1_epi8('/');
 +      const __m128i zero = _mm_set1_epi8('\000');
 +      const __m128i el = _mm_set1_epi8('l');
 +
 +      /* below '*', all characters are forbidden or rare */
 +      const __m128i star_ub = _mm_set1_epi8('*' + 1);
 +
 +      const __m128i colon = _mm_set1_epi8(':');
 +      const __m128i question = _mm_set1_epi8('?');
 +
 +      /* '['..'^' contains 4 characters: 3 forbidden and 1 rare */
 +      const __m128i bracket_lb = _mm_set1_epi8('[' - 1);
 +      const __m128i caret_ub = _mm_set1_epi8('^' + 1);
 +
 +      /* '~' and above are forbidden */
 +      const __m128i tilde_lb = _mm_set1_epi8('~' - 1);
 +
 +      int component_count = 0;
 +      int orig_flags = flags;
 +
 +      if (refname[0] == 0 || refname[0] == '/') {
 +              /* entirely empty ref or initial ref component */
 +              return -1;
 +      }
 +
 +      /*
 +       * Initial ref component of '.'; below we look for /. so we'll
 +       * miss this.
 +       */
 +      if (refname[0] == '.') {
 +              if (refname[1] == '/' || refname[1] == '\0')
 +                      return -1;
 +              if (!(flags & REFNAME_DOT_COMPONENT))
 +                      return -1;
 +      }
 +      while(1) {
 +              __m128i tmp, tmp1, result;
 +              uint64_t mask;
 +
 +              if ((uintptr_t) cp % PAGE_SIZE > PAGE_SIZE - SSE_VECTOR_BYTES  - 1)
 +                      /*
 +                       * End-of-page; fall back to slow method for
 +                       * this entire ref.
 +                       */
 +                      return check_refname_format_bytewise(refname, orig_flags);
 +
 +              tmp = _mm_loadu_si128((__m128i *)cp);
 +              tmp1 = _mm_loadu_si128((__m128i *)(cp + 1));
 +
 +              /*
 +               * This range (note the lt) contains some
 +               * permissible-but-rare characters (including all
 +               * characters >= 128), which we handle later.  It also
 +               * includes \000.
 +               */
 +              result = _mm_cmplt_epi8(tmp, star_ub);
 +
 +              result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, question));
 +              result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, colon));
 +
 +              /* This range contains the permissible ] as bycatch */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpgt_epi8(tmp, bracket_lb),
 +                                            _mm_cmplt_epi8(tmp, caret_ub)));
 +
 +              result = _mm_or_si128(result, _mm_cmpgt_epi8(tmp, tilde_lb));
 +
 +              /* .. */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, dot),
 +                                            _mm_cmpeq_epi8(tmp1, dot)));
 +              /* @{ */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, at),
 +                                            _mm_cmpeq_epi8(tmp1, curly)));
 +              /* // */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, slash),
 +                                            _mm_cmpeq_epi8(tmp1, slash)));
 +              /* trailing / */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, slash),
 +                                            _mm_cmpeq_epi8(tmp1, zero)));
 +              /* .l, beginning of .lock */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, dot),
 +                                            _mm_cmpeq_epi8(tmp1, el)));
 +              /*
 +               * Even though /. is not necessarily an error, we flag
 +               * it anyway. If we find it, we'll check if it's valid
 +               * and if so we'll advance just past it.
 +               */
 +              result = _mm_or_si128(result, _mm_and_si128(
 +                                            _mm_cmpeq_epi8(tmp, slash),
 +                                            _mm_cmpeq_epi8(tmp1, dot)));
 +
 +              mask = _mm_movemask_epi8(result);
 +              if (mask) {
 +                      /*
 +                       * We've found either end-of-string, or some
 +                       * probably-bad character or substring.
 +                       */
 +                      int i = __builtin_ctz(mask);
 +                      switch (refname_disposition[cp[i] & 255]) {
 +                      case 0: /* fall-through */
 +                      case 5:
 +                              /*
 +                               * bycatch: a good character that's in
 +                               * one of the ranges of mostly-forbidden
 +                               * characters
 +                               */
 +                              cp += i + 1;
 +                              break;
 +                      case 1:
 +                              if (cp[i + 1] == '{')
 +                                      return -1;
 +                              cp += i + 1;
 +                              break;
 +                      case 2:
 +                              if (!(flags & REFNAME_ALLOW_ONELEVEL)
 +                                  && !component_count && !strchr(refname, '/'))
 +                                      /* Refname has only one component. */
 +                                      return -1;
 +                              return 0;
 +                      case 3:
 +                              component_count ++;
 +                              /*
 +                               * Even if leading dots are allowed, don't
 +                               * allow "." as a component (".." is
 +                               * prevented by case 4 below).
 +                               */
 +                              if (cp[i + 1] == '.') {
 +                                      if (cp[i + 2] == '\0')
 +                                              return -1;
 +                                      if (flags & REFNAME_DOT_COMPONENT) {
 +                                              /* skip to just after the /. */
 +                                              cp += i + 2;
 +                                              break;
 +                                      }
 +                                      return -1;
 +                              } else if (cp[i + 1] == '/' || cp[i + 1] == '\0')
 +                                      return -1;
 +                              break;
 +                      case 4:
 +                              if (cp[i + 1] == '.' || cp[i + 1] == '\0')
 +                                      return -1;
 +                              /* .lock as end-of-component or end-of-string */
 +                              if ((!strncmp(cp + i, ".lock", 5))
 +                                  && (cp[i + 5] == '/' || cp[i + 5] == 0))
 +                                      return -1;
 +                              cp += 1;
 +                              break;
 +                      case 6:
 +                              if (((cp == refname + i) || cp[i - 1] == '/')
 +                                  && (cp[i + 1] == '/' || cp[i + 1] == 0))
 +                                      if (flags & REFNAME_REFSPEC_PATTERN) {
 +                                              flags &= ~REFNAME_REFSPEC_PATTERN;
 +                                              /* restart after the * */
 +                                              cp += i + 1;
 +                                              continue;
 +                                      }
 +                              /* fall-through */
 +                      case 7:
 +                              return -1;
 +                      }
 +              } else
 +                      cp += SSE_VECTOR_BYTES;
 +      }
 +}
 +
 +#else
 +
 +int check_refname_format (const char *refname, int flags)
 +{
 +      return check_refname_format_bytewise(refname, flags);
 +}
 +
 +#endif
 +
  struct ref_entry;
  
  /*
@@@ -1361,7 -1151,7 +1361,7 @@@ static void read_loose_refs(const char 
  
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
                refdir = *refs->name
@@@ -2226,6 -2016,7 +2226,6 @@@ int dwim_log(const char *str, int len, 
  
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
 -              struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
                const char *ref, *it;
                ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
 -              if (!stat(git_path("logs/%s", path), &st) &&
 -                  S_ISREG(st.st_mode))
 +              if (reflog_exists(path))
                        it = path;
 -              else if (strcmp(ref, path) &&
 -                       !stat(git_path("logs/%s", ref), &st) &&
 -                       S_ISREG(st.st_mode))
 +              else if (strcmp(ref, path) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@@ -3153,133 -2947,122 +3153,133 @@@ int create_symref(const char *ref_targe
        return 0;
  }
  
 -static char *ref_msg(const char *line, const char *endp)
 -{
 -      const char *ep;
 -      line += 82;
 -      ep = memchr(line, '\n', endp - line);
 -      if (!ep)
 -              ep = endp;
 -      return xmemdupz(line, ep - line);
 +struct read_ref_at_cb {
 +      const char *refname;
 +      unsigned long at_time;
 +      int cnt;
 +      int reccnt;
 +      unsigned char *sha1;
 +      int found_it;
 +
 +      unsigned char osha1[20];
 +      unsigned char nsha1[20];
 +      int tz;
 +      unsigned long date;
 +      char **msg;
 +      unsigned long *cutoff_time;
 +      int *cutoff_tz;
 +      int *cutoff_cnt;
 +};
 +
 +static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
 +              const char *email, unsigned long timestamp, int tz,
 +              const char *message, void *cb_data)
 +{
 +      struct read_ref_at_cb *cb = cb_data;
 +
 +      cb->reccnt++;
 +      cb->tz = tz;
 +      cb->date = timestamp;
 +
 +      if (timestamp <= cb->at_time || cb->cnt == 0) {
 +              if (cb->msg)
 +                      *cb->msg = xstrdup(message);
 +              if (cb->cutoff_time)
 +                      *cb->cutoff_time = timestamp;
 +              if (cb->cutoff_tz)
 +                      *cb->cutoff_tz = tz;
 +              if (cb->cutoff_cnt)
 +                      *cb->cutoff_cnt = cb->reccnt - 1;
 +              /*
 +               * we have not yet updated cb->[n|o]sha1 so they still
 +               * hold the values for the previous record.
 +               */
 +              if (!is_null_sha1(cb->osha1)) {
 +                      hashcpy(cb->sha1, nsha1);
 +                      if (hashcmp(cb->osha1, nsha1))
 +                              warning("Log for ref %s has gap after %s.",
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +              }
 +              else if (cb->date == cb->at_time)
 +                      hashcpy(cb->sha1, nsha1);
 +              else if (hashcmp(nsha1, cb->sha1))
 +                      warning("Log for ref %s unexpectedly ended on %s.",
 +                              cb->refname, show_date(cb->date, cb->tz,
 +                                                 DATE_RFC2822));
 +              hashcpy(cb->osha1, osha1);
 +              hashcpy(cb->nsha1, nsha1);
 +              cb->found_it = 1;
 +              return 1;
 +      }
 +      hashcpy(cb->osha1, osha1);
 +      hashcpy(cb->nsha1, nsha1);
 +      if (cb->cnt > 0)
 +              cb->cnt--;
 +      return 0;
 +}
 +
 +static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
 +                                const char *email, unsigned long timestamp,
 +                                int tz, const char *message, void *cb_data)
 +{
 +      struct read_ref_at_cb *cb = cb_data;
 +
 +      if (cb->msg)
 +              *cb->msg = xstrdup(message);
 +      if (cb->cutoff_time)
 +              *cb->cutoff_time = timestamp;
 +      if (cb->cutoff_tz)
 +              *cb->cutoff_tz = tz;
 +      if (cb->cutoff_cnt)
 +              *cb->cutoff_cnt = cb->reccnt;
 +      hashcpy(cb->sha1, osha1);
 +      if (is_null_sha1(cb->sha1))
 +              hashcpy(cb->sha1, nsha1);
 +      /* We just want the first entry */
 +      return 1;
  }
  
  int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                unsigned char *sha1, char **msg,
                unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
  {
 -      const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
 -      char *tz_c;
 -      int logfd, tz, reccnt = 0;
 -      struct stat st;
 -      unsigned long date;
 -      unsigned char logged_sha1[20];
 -      void *log_mapped;
 -      size_t mapsz;
 +      struct read_ref_at_cb cb;
  
 -      logfile = git_path("logs/%s", refname);
 -      logfd = open(logfile, O_RDONLY, 0);
 -      if (logfd < 0)
 -              die_errno("Unable to read log '%s'", logfile);
 -      fstat(logfd, &st);
 -      if (!st.st_size)
 -              die("Log %s is empty.", logfile);
 -      mapsz = xsize_t(st.st_size);
 -      log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);
 -      logdata = log_mapped;
 -      close(logfd);
 +      memset(&cb, 0, sizeof(cb));
 +      cb.refname = refname;
 +      cb.at_time = at_time;
 +      cb.cnt = cnt;
 +      cb.msg = msg;
 +      cb.cutoff_time = cutoff_time;
 +      cb.cutoff_tz = cutoff_tz;
 +      cb.cutoff_cnt = cutoff_cnt;
 +      cb.sha1 = sha1;
 +
 +      for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
 +
 +      if (!cb.reccnt)
 +              die("Log for %s is empty.", refname);
 +      if (cb.found_it)
 +              return 0;
 +
 +      for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
  
 -      lastrec = NULL;
 -      rec = logend = logdata + st.st_size;
 -      while (logdata < rec) {
 -              reccnt++;
 -              if (logdata < rec && *(rec-1) == '\n')
 -                      rec--;
 -              lastgt = NULL;
 -              while (logdata < rec && *(rec-1) != '\n') {
 -                      rec--;
 -                      if (*rec == '>')
 -                              lastgt = rec;
 -              }
 -              if (!lastgt)
 -                      die("Log %s is corrupt.", logfile);
 -              date = strtoul(lastgt + 1, &tz_c, 10);
 -              if (date <= at_time || cnt == 0) {
 -                      tz = strtoul(tz_c, NULL, 10);
 -                      if (msg)
 -                              *msg = ref_msg(rec, logend);
 -                      if (cutoff_time)
 -                              *cutoff_time = date;
 -                      if (cutoff_tz)
 -                              *cutoff_tz = tz;
 -                      if (cutoff_cnt)
 -                              *cutoff_cnt = reccnt - 1;
 -                      if (lastrec) {
 -                              if (get_sha1_hex(lastrec, logged_sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (get_sha1_hex(rec + 41, sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (hashcmp(logged_sha1, sha1)) {
 -                                      warning("Log %s has gap after %s.",
 -                                              logfile, show_date(date, tz, DATE_RFC2822));
 -                              }
 -                      }
 -                      else if (date == at_time) {
 -                              if (get_sha1_hex(rec + 41, sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                      }
 -                      else {
 -                              if (get_sha1_hex(rec + 41, logged_sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (hashcmp(logged_sha1, sha1)) {
 -                                      warning("Log %s unexpectedly ended on %s.",
 -                                              logfile, show_date(date, tz, DATE_RFC2822));
 -                              }
 -                      }
 -                      munmap(log_mapped, mapsz);
 -                      return 0;
 -              }
 -              lastrec = rec;
 -              if (cnt > 0)
 -                      cnt--;
 -      }
 -
 -      rec = logdata;
 -      while (rec < logend && *rec != '>' && *rec != '\n')
 -              rec++;
 -      if (rec == logend || *rec == '\n')
 -              die("Log %s is corrupt.", logfile);
 -      date = strtoul(rec + 1, &tz_c, 10);
 -      tz = strtoul(tz_c, NULL, 10);
 -      if (get_sha1_hex(logdata, sha1))
 -              die("Log %s is corrupt.", logfile);
 -      if (is_null_sha1(sha1)) {
 -              if (get_sha1_hex(logdata + 41, sha1))
 -                      die("Log %s is corrupt.", logfile);
 -      }
 -      if (msg)
 -              *msg = ref_msg(logdata, logend);
 -      munmap(log_mapped, mapsz);
 -
 -      if (cutoff_time)
 -              *cutoff_time = date;
 -      if (cutoff_tz)
 -              *cutoff_tz = tz;
 -      if (cutoff_cnt)
 -              *cutoff_cnt = reccnt;
        return 1;
  }
  
 +int reflog_exists(const char *refname)
 +{
 +      struct stat st;
 +
 +      return !lstat(git_path("logs/%s", refname), &st) &&
 +              S_ISREG(st.st_mode);
 +}
 +
 +int delete_reflog(const char *refname)
 +{
 +      return remove_path(git_path("logs/%s", refname));
 +}
 +
  static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
  {
        unsigned char osha1[20], nsha1[20];
@@@ -3432,7 -3215,7 +3432,7 @@@ static int do_for_each_reflog(struct st
  
                if (de->d_name[0] == '.')
                        continue;
-               if (has_extension(de->d_name, ".lock"))
+               if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(name, de->d_name);
                if (stat(git_path("logs/%s", name->buf), &st) < 0) {
@@@ -3477,9 -3260,9 +3477,9 @@@ static struct ref_lock *update_ref_lock
        if (!lock) {
                const char *str = "Cannot lock the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
        }
        return lock;
@@@ -3492,118 -3275,15 +3492,118 @@@ static int update_ref_write(const char 
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
                return 1;
        }
        return 0;
  }
  
 +/**
 + * Information needed for a single ref update.  Set new_sha1 to the
 + * new value or to zero to delete the ref.  To check the old value
 + * while locking the ref, set have_old to 1 and set old_sha1 to the
 + * value or to zero to ensure the ref does not exist before update.
 + */
 +struct ref_update {
 +      unsigned char new_sha1[20];
 +      unsigned char old_sha1[20];
 +      int flags; /* REF_NODEREF? */
 +      int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 +      struct ref_lock *lock;
 +      int type;
 +      const char refname[FLEX_ARRAY];
 +};
 +
 +/*
 + * Data structure for holding a reference transaction, which can
 + * consist of checks and updates to multiple references, carried out
 + * as atomically as possible.  This structure is opaque to callers.
 + */
 +struct ref_transaction {
 +      struct ref_update **updates;
 +      size_t alloc;
 +      size_t nr;
 +};
 +
 +struct ref_transaction *ref_transaction_begin(void)
 +{
 +      return xcalloc(1, sizeof(struct ref_transaction));
 +}
 +
 +static void ref_transaction_free(struct ref_transaction *transaction)
 +{
 +      int i;
 +
 +      for (i = 0; i < transaction->nr; i++)
 +              free(transaction->updates[i]);
 +
 +      free(transaction->updates);
 +      free(transaction);
 +}
 +
 +void ref_transaction_rollback(struct ref_transaction *transaction)
 +{
 +      ref_transaction_free(transaction);
 +}
 +
 +static struct ref_update *add_update(struct ref_transaction *transaction,
 +                                   const char *refname)
 +{
 +      size_t len = strlen(refname);
 +      struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);
 +
 +      strcpy((char *)update->refname, refname);
 +      ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 +      transaction->updates[transaction->nr++] = update;
 +      return update;
 +}
 +
 +void ref_transaction_update(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1, unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      hashcpy(update->new_sha1, new_sha1);
 +      update->flags = flags;
 +      update->have_old = have_old;
 +      if (have_old)
 +              hashcpy(update->old_sha1, old_sha1);
 +}
 +
 +void ref_transaction_create(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1,
 +                          int flags)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      assert(!is_null_sha1(new_sha1));
 +      hashcpy(update->new_sha1, new_sha1);
 +      hashclr(update->old_sha1);
 +      update->flags = flags;
 +      update->have_old = 1;
 +}
 +
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      update->flags = flags;
 +      update->have_old = have_old;
 +      if (have_old) {
 +              assert(!is_null_sha1(old_sha1));
 +              hashcpy(update->old_sha1, old_sha1);
 +      }
 +}
 +
  int update_ref(const char *action, const char *refname,
               const unsigned char *sha1, const unsigned char *oldval,
               int flags, enum action_on_err onerr)
@@@ -3619,7 -3299,7 +3619,7 @@@ static int ref_update_compare(const voi
  {
        const struct ref_update * const *u1 = r1;
        const struct ref_update * const *u2 = r2;
 -      return strcmp((*u1)->ref_name, (*u2)->ref_name);
 +      return strcmp((*u1)->refname, (*u2)->refname);
  }
  
  static int ref_update_reject_duplicates(struct ref_update **updates, int n,
  {
        int i;
        for (i = 1; i < n; i++)
 -              if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
 +              if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        const char *str =
                                "Multiple updates for ref '%s' not allowed.";
                        switch (onerr) {
 -                      case MSG_ON_ERR:
 -                              error(str, updates[i]->ref_name); break;
 -                      case DIE_ON_ERR:
 -                              die(str, updates[i]->ref_name); break;
 -                      case QUIET_ON_ERR:
 +                      case UPDATE_REFS_MSG_ON_ERR:
 +                              error(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_DIE_ON_ERR:
 +                              die(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_QUIET_ON_ERR:
                                break;
                        }
                        return 1;
        return 0;
  }
  
 -int update_refs(const char *action, const struct ref_update **updates_orig,
 -              int n, enum action_on_err onerr)
 +int ref_transaction_commit(struct ref_transaction *transaction,
 +                         const char *msg, enum action_on_err onerr)
  {
        int ret = 0, delnum = 0, i;
 -      struct ref_update **updates;
 -      int *types;
 -      struct ref_lock **locks;
        const char **delnames;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
  
 -      if (!updates_orig || !n)
 +      if (!n)
                return 0;
  
        /* Allocate work space */
 -      updates = xmalloc(sizeof(*updates) * n);
 -      types = xmalloc(sizeof(*types) * n);
 -      locks = xcalloc(n, sizeof(*locks));
        delnames = xmalloc(sizeof(*delnames) * n);
  
        /* Copy, sort, and reject duplicate refs */
 -      memcpy(updates, updates_orig, sizeof(*updates) * n);
        qsort(updates, n, sizeof(*updates), ref_update_compare);
        ret = ref_update_reject_duplicates(updates, n, onerr);
        if (ret)
  
        /* Acquire all locks while verifying old values */
        for (i = 0; i < n; i++) {
 -              locks[i] = update_ref_lock(updates[i]->ref_name,
 -                                         (updates[i]->have_old ?
 -                                          updates[i]->old_sha1 : NULL),
 -                                         updates[i]->flags,
 -                                         &types[i], onerr);
 -              if (!locks[i]) {
 +              struct ref_update *update = updates[i];
 +
 +              update->lock = update_ref_lock(update->refname,
 +                                             (update->have_old ?
 +                                              update->old_sha1 : NULL),
 +                                             update->flags,
 +                                             &update->type, onerr);
 +              if (!update->lock) {
                        ret = 1;
                        goto cleanup;
                }
        }
  
        /* Perform updates first so live commits remain referenced */
 -      for (i = 0; i < n; i++)
 -              if (!is_null_sha1(updates[i]->new_sha1)) {
 -                      ret = update_ref_write(action,
 -                                             updates[i]->ref_name,
 -                                             updates[i]->new_sha1,
 -                                             locks[i], onerr);
 -                      locks[i] = NULL; /* freed by update_ref_write */
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (!is_null_sha1(update->new_sha1)) {
 +                      ret = update_ref_write(msg,
 +                                             update->refname,
 +                                             update->new_sha1,
 +                                             update->lock, onerr);
 +                      update->lock = NULL; /* freed by update_ref_write */
                        if (ret)
                                goto cleanup;
                }
 +      }
  
        /* Perform deletes now that updates are safely completed */
 -      for (i = 0; i < n; i++)
 -              if (locks[i]) {
 -                      delnames[delnum++] = locks[i]->ref_name;
 -                      ret |= delete_ref_loose(locks[i], types[i]);
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (update->lock) {
 +                      delnames[delnum++] = update->lock->ref_name;
 +                      ret |= delete_ref_loose(update->lock, update->type);
                }
 +      }
 +
        ret |= repack_without_refs(delnames, delnum);
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
  
  cleanup:
        for (i = 0; i < n; i++)
 -              if (locks[i])
 -                      unlock_ref(locks[i]);
 -      free(updates);
 -      free(types);
 -      free(locks);
 +              if (updates[i]->lock)
 +                      unlock_ref(updates[i]->lock);
        free(delnames);
 +      ref_transaction_free(transaction);
        return ret;
  }
  
diff --combined sha1_file.c
index a38854ce553c1e59294d5542a37a5404ccd66dc5,129a4c52df253ed8c17d4afbc80458e5917f41d3..4127ae138de6bb002645aa83af00681b1805d982
@@@ -315,8 -315,7 +315,8 @@@ static int link_alt_odb_entry(const cha
         * thing twice, or object directory itself.
         */
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              if (!memcmp(ent->base, alt->base, pfxlen)) {
 +              if (pfxlen == alt->name - alt->base - 1 &&
 +                  !memcmp(ent->base, alt->base, pfxlen)) {
                        free(ent);
                        return -1;
                }
@@@ -1178,48 -1177,42 +1178,42 @@@ static void report_pack_garbage(struct 
  
  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_)
diff --combined strbuf.c
index 12c78656ca9ddb51fb62b95be29d18056911803f,63356ccd1b60bfddbff3dcb1d816d3e0f0d16ce4..33018d847f08424211c02cff8002680f5a2f02e9
+++ b/strbuf.c
@@@ -1,6 -1,5 +1,6 @@@
  #include "cache.h"
  #include "refs.h"
 +#include "utf8.h"
  
  int starts_with(const char *str, const char *prefix)
  {
                        return 0;
  }
  
- int ends_with(const char *str, const char *suffix)
- {
-       int len = strlen(str), suflen = strlen(suffix);
-       if (len < suflen)
-               return 0;
-       else
-               return !strcmp(str + len - suflen, suffix);
- }
  /*
   * Used as the default ->buf value, so that people can always assume
   * buf is non NULL and ->buf is NUL terminated even for a freshly
@@@ -79,8 -69,15 +70,8 @@@ void strbuf_grow(struct strbuf *sb, siz
  
  void strbuf_trim(struct strbuf *sb)
  {
 -      char *b = sb->buf;
 -      while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
 -              sb->len--;
 -      while (sb->len > 0 && isspace(*b)) {
 -              b++;
 -              sb->len--;
 -      }
 -      memmove(sb->buf, b, sb->len);
 -      sb->buf[sb->len] = '\0';
 +      strbuf_rtrim(sb);
 +      strbuf_ltrim(sb);
  }
  void strbuf_rtrim(struct strbuf *sb)
  {
@@@ -100,29 -97,6 +91,29 @@@ void strbuf_ltrim(struct strbuf *sb
        sb->buf[sb->len] = '\0';
  }
  
 +int strbuf_reencode(struct strbuf *sb, const char *from, const char *to)
 +{
 +      char *out;
 +      int len;
 +
 +      if (same_encoding(from, to))
 +              return 0;
 +
 +      out = reencode_string_len(sb->buf, sb->len, to, from, &len);
 +      if (!out)
 +              return -1;
 +
 +      strbuf_attach(sb, out, len, len);
 +      return 0;
 +}
 +
 +void strbuf_tolower(struct strbuf *sb)
 +{
 +      char *p = sb->buf, *end = sb->buf + sb->len;
 +      for (; p < end; p++)
 +              *p = tolower(*p);
 +}
 +
  struct strbuf **strbuf_split_buf(const char *str, size_t slen,
                                 int terminator, int max)
  {
@@@ -587,35 -561,3 +578,35 @@@ int fprintf_ln(FILE *fp, const char *fm
                return -1;
        return ret + 1;
  }
 +
 +char *xstrdup_tolower(const char *string)
 +{
 +      char *result;
 +      size_t len, i;
 +
 +      len = strlen(string);
 +      result = xmalloc(len + 1);
 +      for (i = 0; i < len; i++)
 +              result[i] = tolower(string[i]);
 +      result[i] = '\0';
 +      return result;
 +}
 +
 +char *xstrvfmt(const char *fmt, va_list ap)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      strbuf_vaddf(&buf, fmt, ap);
 +      return strbuf_detach(&buf, NULL);
 +}
 +
 +char *xstrfmt(const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *ret;
 +
 +      va_start(ap, fmt);
 +      ret = xstrvfmt(fmt, ap);
 +      va_end(ap);
 +
 +      return ret;
 +}
diff --combined strbuf.h
index a594c24b2b0e9e82c16de1bb787758d561cb2e7b,1d768c112468fd1a00db55e079fe19e6f7ba3bfd..a7c0192e9ee392bd232b325692a9111b3e160e84
+++ b/strbuf.h
@@@ -45,10 -45,17 +45,19 @@@ static inline void strbuf_setlen(struc
  extern void strbuf_trim(struct strbuf *);
  extern void strbuf_rtrim(struct strbuf *);
  extern void strbuf_ltrim(struct strbuf *);
 +extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 +extern void strbuf_tolower(struct strbuf *sb);
  extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
  
+ static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
+ {
+       if (strip_suffix_mem(sb->buf, &sb->len, suffix)) {
+               strbuf_setlen(sb, sb->len);
+               return 1;
+       } else
+               return 0;
+ }
  /*
   * Split str (of length slen) at the specified terminator character.
   * Return a null-terminated array of pointers to strbuf objects
@@@ -185,15 -192,4 +194,15 @@@ extern int printf_ln(const char *fmt, .
  __attribute__((format (printf,2,3)))
  extern int fprintf_ln(FILE *fp, const char *fmt, ...);
  
 +char *xstrdup_tolower(const char *);
 +
 +/*
 + * Create a newly allocated string using printf format. You can do this easily
 + * with a strbuf, but this provides a shortcut to save a few lines.
 + */
 +__attribute__((format (printf, 1, 0)))
 +char *xstrvfmt(const char *fmt, va_list ap);
 +__attribute__((format (printf, 1, 2)))
 +char *xstrfmt(const char *fmt, ...);
 +
  #endif /* STRBUF_H */