Merge branch 'jk/alloc-commit-id'
authorJunio C Hamano <gitster@pobox.com>
Tue, 22 Jul 2014 17:59:24 +0000 (10:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 22 Jul 2014 17:59:25 +0000 (10:59 -0700)
Make sure all in-core commit objects are assigned a unique number
so that they can be annotated using the commit-slab API.

* jk/alloc-commit-id:
diff-tree: avoid lookup_unknown_object
object_as_type: set commit index
alloc: factor out commit index
add object_as_type helper for casting objects
parse_object_buffer: do not set object type
move setting of object->type to alloc_* functions
alloc: write out allocator definitions
alloc.c: remove the alloc_raw_commit_node() function

1  2 
builtin/blame.c
builtin/diff-tree.c
cache.h
commit.c
object.c
refs.c
diff --combined builtin/blame.c
index 32ce05f6159bb9a282e20f314a7d4aa079eb892e,8f3e311123f44d277cd750d358df7b92ad3dd782..23212cbe87623c13772e5743a6b03a57a91749b0
@@@ -2287,7 -2287,6 +2287,6 @@@ static struct commit *fake_working_tree
        commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
-       commit->object.type = OBJ_COMMIT;
        parent_tail = &commit->parents;
  
        if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
         * right now, but someday we might optimize diff-index --cached
         * with cache-tree information.
         */
 -      cache_tree_invalidate_path(active_cache_tree, path);
 +      cache_tree_invalidate_path(&the_index, path);
  
        return commit;
  }
@@@ -2707,8 -2706,11 +2706,8 @@@ parse_done
                die("revision walk setup failed");
  
        if (is_null_sha1(sb.final->object.sha1)) {
 -              char *buf;
                o = sb.final->util;
 -              buf = xmalloc(o->file.size + 1);
 -              memcpy(buf, o->file.ptr, o->file.size + 1);
 -              sb.final_buf = buf;
 +              sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
        }
        else {
diff --combined builtin/diff-tree.c
index ce0e019e0caa358e1f5e39e5ed51bec3945197e0,dddd0f91886d0ce83c06982e9c975e78617f9ac2..1c4ad6223eb9547666ec21f81f410ef1a459ec92
@@@ -22,10 -22,14 +22,10 @@@ static int stdin_diff_commit(struct com
        if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
                /* Graft the fake parents locally to the commit */
                int pos = 41;
 -              struct commit_list **pptr, *parents;
 +              struct commit_list **pptr;
  
                /* Free the real parent list */
 -              for (parents = commit->parents; parents; ) {
 -                      struct commit_list *tmp = parents->next;
 -                      free(parents);
 -                      parents = tmp;
 -              }
 +              free_commit_list(commit->parents);
                commit->parents = NULL;
                pptr = &(commit->parents);
                while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
@@@ -68,9 -72,7 +68,7 @@@ static int diff_tree_stdin(char *line
        line[len-1] = 0;
        if (get_sha1_hex(line, sha1))
                return -1;
-       obj = lookup_unknown_object(sha1);
-       if (!obj || !obj->parsed)
-               obj = parse_object(sha1);
+       obj = parse_object(sha1);
        if (!obj)
                return -1;
        if (obj->type == OBJ_COMMIT)
diff --combined cache.h
index 8ae30d5a8510d246921de7ea1bf1300ee11dd1bf,42a5e865b588d39836a04b8a022e53ae8ce76d5d..fcb511db70f7703f2b29dbc89dcf703065c823fe
+++ b/cache.h
@@@ -7,7 -7,6 +7,7 @@@
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
 +#include "trace.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -151,7 -150,6 +151,7 @@@ struct cache_entry 
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
 +      unsigned int index;     /* for link extension */
        unsigned char sha1[20];
        char name[FLEX_ARRAY]; /* more */
  };
  #define CE_STAGESHIFT 12
  
  /*
 - * Range 0xFFFF0000 in ce_flags is divided into
 + * Range 0xFFFF0FFF in ce_flags is divided into
   * two parts: in-memory flags and on-disk ones.
   * Flags in CE_EXTENDED_FLAGS will get saved on-disk
   * if you want to save a new flag, add it in
  /* used to temporarily mark paths matched by pathspecs */
  #define CE_MATCHED           (1 << 26)
  
 +#define CE_UPDATE_IN_BASE    (1 << 27)
 +#define CE_STRIP_NAME        (1 << 28)
 +
  /*
   * Extended on-disk flags
   */
@@@ -288,22 -283,12 +288,22 @@@ static inline unsigned int canon_mode(u
  
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
  
 +#define SOMETHING_CHANGED     (1 << 0) /* unclassified changes go here */
 +#define CE_ENTRY_CHANGED      (1 << 1)
 +#define CE_ENTRY_REMOVED      (1 << 2)
 +#define CE_ENTRY_ADDED                (1 << 3)
 +#define RESOLVE_UNDO_CHANGED  (1 << 4)
 +#define CACHE_TREE_CHANGED    (1 << 5)
 +#define SPLIT_INDEX_ORDERED   (1 << 6)
 +
 +struct split_index;
  struct index_state {
        struct cache_entry **cache;
        unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
 +      struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
@@@ -332,6 -317,7 +332,6 @@@ extern void free_name_hash(struct index
  #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
  #define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
 -#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
  #define discard_cache() discard_index(&the_index)
  #define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
@@@ -486,17 -472,12 +486,17 @@@ extern int daemonize(void)
        } while (0)
  
  /* Initialize and use the cache information */
 +struct lock_file;
  extern int read_index(struct index_state *);
  extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 +extern int do_read_index(struct index_state *istate, const char *path,
 +                       int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 -extern int write_index(struct index_state *, int newfd);
 +#define COMMIT_LOCK           (1 << 0)
 +#define CLOSE_LOCK            (1 << 1)
 +extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
@@@ -508,7 -489,6 +508,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
 +#define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  extern int remove_index_entry_at(struct index_state *, int pos);
@@@ -579,8 -559,6 +579,8 @@@ struct lock_file 
  #define LOCK_DIE_ON_ERROR 1
  #define LOCK_NODEREF 2
  extern int unable_to_lock_error(const char *path, int err);
 +extern void unable_to_lock_message(const char *path, int err,
 +                                 struct strbuf *buf);
  extern NORETURN void unable_to_lock_index_die(const char *path, int err);
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@@@ -588,6 -566,7 +588,6 @@@ extern int commit_lock_file(struct lock
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
 -extern int commit_locked_index(struct lock_file *);
  extern void set_alternate_index_output(const char *);
  extern int close_lock_file(struct lock_file *);
  extern void rollback_lock_file(struct lock_file *);
@@@ -998,7 -977,7 +998,7 @@@ extern int read_ref(const char *refname
   * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
   * give up and return NULL.
   *
 - * errno is sometimes set on errors, but not always.
 + * errno is set to something meaningful on error.
   */
  extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
  extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
@@@ -1099,7 -1078,6 +1099,7 @@@ const char *show_ident_date(const struc
  extern int ident_cmp(const struct ident_split *, const struct ident_split *);
  
  struct checkout {
 +      struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
        unsigned force:1,
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
  
  struct cache_def {
 -      char path[PATH_MAX + 1];
 -      int len;
 +      struct strbuf path;
        int flags;
        int track_flags;
        int prefix_len_stat_func;
  };
 +#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
 +static inline void cache_def_clear(struct cache_def *cache)
 +{
 +      strbuf_release(&cache->path);
 +}
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
@@@ -1402,8 -1376,19 +1402,9 @@@ extern void *alloc_commit_node(void)
  extern void *alloc_tag_node(void);
  extern void *alloc_object_node(void);
  extern void alloc_report(void);
+ extern unsigned int alloc_commit_index(void);
  
 -/* trace.c */
 -__attribute__((format (printf, 1, 2)))
 -extern void trace_printf(const char *format, ...);
 -__attribute__((format (printf, 2, 3)))
 -extern void trace_argv_printf(const char **argv, const char *format, ...);
 -extern void trace_repo_setup(const char *prefix);
 -extern int trace_want(const char *key);
 -__attribute__((format (printf, 2, 3)))
 -extern void trace_printf_key(const char *key, const char *fmt, ...);
 -extern void trace_strbuf(const char *key, const struct strbuf *buf);
 -
 +/* pkt-line.c */
  void packet_trace_identity(const char *prog);
  
  /* add */
diff --combined commit.c
index f1c9d0e2b99bd6f21ce0a0debae646eb677fccf5,65179f96eecc85082f9bf3f0312676878d92861d..90a6d2974f321cf045fa801cb3409b8dffeeaddb
+++ b/commit.c
@@@ -18,19 -18,6 +18,6 @@@ int save_commit_buffer = 1
  
  const char *commit_type = "commit";
  
- static struct commit *check_commit(struct object *obj,
-                                  const unsigned char *sha1,
-                                  int quiet)
- {
-       if (obj->type != OBJ_COMMIT) {
-               if (!quiet)
-                       error("Object %s is a %s, not a commit",
-                             sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct commit *) obj;
- }
  struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
                                              int quiet)
  {
@@@ -38,7 -25,7 +25,7 @@@
  
        if (!obj)
                return NULL;
-       return check_commit(obj, sha1, quiet);
+       return object_as_type(obj, OBJ_COMMIT, quiet);
  }
  
  struct commit *lookup_commit_reference(const unsigned char *sha1)
@@@ -61,13 -48,9 +48,9 @@@ struct commit *lookup_commit_or_die(con
  struct commit *lookup_commit(const unsigned char *sha1)
  {
        struct object *obj = lookup_object(sha1);
-       if (!obj) {
-               struct commit *c = alloc_commit_node();
-               return create_object(sha1, OBJ_COMMIT, c);
-       }
-       if (!obj->type)
-               obj->type = OBJ_COMMIT;
-       return check_commit(obj, sha1, 0);
+       if (!obj)
+               return create_object(sha1, alloc_commit_node());
+       return object_as_type(obj, OBJ_COMMIT, 0);
  }
  
  struct commit *lookup_commit_reference_by_name(const char *name)
@@@ -447,7 -430,12 +430,7 @@@ struct commit_list *copy_commit_list(st
        struct commit_list *head = NULL;
        struct commit_list **pp = &head;
        while (list) {
 -              struct commit_list *new;
 -              new = xmalloc(sizeof(struct commit_list));
 -              new->item = list->item;
 -              new->next = NULL;
 -              *pp = new;
 -              pp = &new->next;
 +              pp = commit_list_append(list->item, pp);
                list = list->next;
        }
        return head;
@@@ -609,7 -597,8 +592,7 @@@ static void record_author_date(struct a
  
        for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
 -              ident_line = skip_prefix(buf, "author ");
 -              if (!ident_line) {
 +              if (!skip_prefix(buf, "author ", &ident_line)) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
@@@ -1231,7 -1220,8 +1214,7 @@@ static void parse_gpg_output(struct sig
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
  
 -              found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
 -              if (!found) {
 +              if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
                        found = strstr(buf, sigcheck_gpg_status[i].check);
                        if (!found)
                                continue;
@@@ -1265,7 -1255,6 +1248,7 @@@ void check_commit_signature(const struc
                                      &gpg_output, &gpg_status);
        if (status && !gpg_output.len)
                goto out;
 +      sigc->payload = strbuf_detach(&payload, NULL);
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
@@@ -1310,19 -1299,6 +1293,19 @@@ struct commit_extra_header *read_commit
        return extra;
  }
  
 +void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
 +{
 +      struct commit_extra_header *extra, *to_free;
 +
 +      to_free = read_commit_extra_headers(commit, NULL);
 +      for (extra = to_free; extra; extra = extra->next) {
 +              if (strcmp(extra->key, "mergetag"))
 +                      continue; /* not a merge tag */
 +              fn(commit, extra, data);
 +      }
 +      free_commit_extra_headers(to_free);
 +}
 +
  static inline int standard_header_field(const char *field, size_t len)
  {
        return ((len == 4 && !memcmp(field, "tree ", 5)) ||
diff --combined object.c
index 91c7c867b9943f6e1b5b7fef560d50450c8adc5a,69fbbbf504d861fac42ee50bc04a49ede1f60028..a16b9f9e936d060ba190b6c7e82ff5f25795f490
+++ b/object.c
@@@ -50,7 -50,18 +50,7 @@@ int type_from_string(const char *str
   */
  static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
  {
 -      unsigned int hash;
 -
 -      /*
 -       * Since the sha1 is essentially random, we just take the
 -       * required number of bits directly from the first
 -       * sizeof(unsigned int) bytes of sha1.  First we have to copy
 -       * the bytes into a properly aligned integer.  If we cared
 -       * about getting consistent results across architectures, we
 -       * would have to call ntohl() here, too.
 -       */
 -      memcpy(&hash, sha1, sizeof(unsigned int));
 -      return hash & (n - 1);
 +      return sha1hash(sha1) & (n - 1);
  }
  
  /*
@@@ -130,13 -141,12 +130,12 @@@ static void grow_object_hash(void
        obj_hash_size = new_hash_size;
  }
  
- void *create_object(const unsigned char *sha1, int type, void *o)
+ void *create_object(const unsigned char *sha1, void *o)
  {
        struct object *obj = o;
  
        obj->parsed = 0;
        obj->used = 0;
-       obj->type = type;
        obj->flags = 0;
        hashcpy(obj->sha1, sha1);
  
        return obj;
  }
  
+ void *object_as_type(struct object *obj, enum object_type type, int quiet)
+ {
+       if (obj->type == type)
+               return obj;
+       else if (obj->type == OBJ_NONE) {
+               if (type == OBJ_COMMIT)
+                       ((struct commit *)obj)->index = alloc_commit_index();
+               obj->type = type;
+               return obj;
+       }
+       else {
+               if (!quiet)
+                       error("object %s is a %s, not a %s",
+                             sha1_to_hex(obj->sha1),
+                             typename(obj->type), typename(type));
+               return NULL;
+       }
+ }
  struct object *lookup_unknown_object(const unsigned char *sha1)
  {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               obj = create_object(sha1, OBJ_NONE, alloc_object_node());
+               obj = create_object(sha1, alloc_object_node());
        return obj;
  }
  
@@@ -203,8 -232,6 +221,6 @@@ struct object *parse_object_buffer(cons
                warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
                obj = NULL;
        }
-       if (obj && obj->type == OBJ_NONE)
-               obj->type = type;
        return obj;
  }
  
diff --combined refs.c
index 3792314f3bfd0e88b91a245240cda3ec1ac24af3,7343565354f2bfe2338264ad6acfa3117fc653f7..84b9070b8dca5eb94fbe9b7d7c463a0ea2af92eb
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -7,27 -7,21 +7,27 @@@
  
  /*
   * 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: End-of-component
 - * 2: ., look for a preceding . to reject .. in refs
 - * 3: {, look for a preceding @ to reject @{ in refs
 - * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
 + * 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] = {
 -      1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 -      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 -      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
 -      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
 +      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, 4, 4, 0, 4, 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, 3, 0, 0, 4, 4
 +      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
  };
  
  /*
@@@ -39,9 -33,8 +39,9 @@@
   * - 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)
   */
  static int check_refname_component(const char *refname, int flags)
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
                switch (disp) {
 -              case 1:
 +              case 2: /* fall-through */
 +              case 3:
                        goto out;
 -              case 2:
 +              case 4:
                        if (last == '.')
                                return -1; /* Refname contains "..". */
                        break;
 -              case 3:
 +              case 5:
                        if (last == '@')
                                return -1; /* Refname contains "@{". */
                        break;
 -              case 4:
 +              case 6: /* fall-through */
 +              case 7:
                        return -1;
                }
                last = ch;
@@@ -88,7 -79,7 +88,7 @@@ out
        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 -1162,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
@@@ -1533,7 -1334,6 +1533,7 @@@ static const char *handle_missing_loose
        }
  }
  
 +/* This function needs to return a meaningful errno on failure */
  const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
        if (flag)
                *flag = 0;
  
 -      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 +      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 +              errno = EINVAL;
                return NULL;
 +      }
  
        for (;;) {
                char path[PATH_MAX];
                char *buf;
                int fd;
  
 -              if (--depth < 0)
 +              if (--depth < 0) {
 +                      errno = ELOOP;
                        return NULL;
 +              }
  
                git_snpath(path, sizeof(path), "%s", refname);
  
                                return NULL;
                }
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
 -              close(fd);
 -              if (len < 0)
 +              if (len < 0) {
 +                      int save_errno = errno;
 +                      close(fd);
 +                      errno = save_errno;
                        return NULL;
 +              }
 +              close(fd);
                while (len && isspace(buffer[len-1]))
                        len--;
                buffer[len] = '\0';
                            (buffer[40] != '\0' && !isspace(buffer[40]))) {
                                if (flag)
                                        *flag |= REF_ISBROKEN;
 +                              errno = EINVAL;
                                return NULL;
                        }
                        return refname;
                if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
                        if (flag)
                                *flag |= REF_ISBROKEN;
 +                      errno = EINVAL;
                        return NULL;
                }
                refname = strcpy(refname_buffer, buf);
@@@ -1741,9 -1531,8 +1741,8 @@@ static enum peel_status peel_object(con
  
        if (o->type == OBJ_NONE) {
                int type = sha1_object_info(name, NULL);
-               if (type < 0)
+               if (type < 0 || !object_as_type(o, type, 0))
                        return PEEL_INVALID;
-               o->type = type;
        }
  
        if (o->type != OBJ_TAG)
@@@ -2142,22 -1931,18 +2141,22 @@@ int refname_match(const char *abbrev_na
        return 0;
  }
  
 +/* This function should make sure errno is meaningful on error */
  static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
  {
        if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
 +              int save_errno = errno;
                error("Can't verify ref %s", lock->ref_name);
                unlock_ref(lock);
 +              errno = save_errno;
                return NULL;
        }
        if (hashcmp(lock->old_sha1, old_sha1)) {
                error("Ref %s is at %s but expected %s", lock->ref_name,
                        sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
                unlock_ref(lock);
 +              errno = EBUSY;
                return NULL;
        }
        return lock;
@@@ -2170,16 -1955,14 +2169,16 @@@ static int remove_empty_directories(con
         * only empty directories), remove them.
         */
        struct strbuf path;
 -      int result;
 +      int result, save_errno;
  
        strbuf_init(&path, 20);
        strbuf_addstr(&path, file);
  
        result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
 +      save_errno = errno;
  
        strbuf_release(&path);
 +      errno = save_errno;
  
        return result;
  }
@@@ -2268,7 -2051,6 +2267,7 @@@ int dwim_log(const char *str, int len, 
        return logs_found;
  }
  
 +/* This function should make sure errno is meaningful on error */
  static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
                                            int flags, int *type_p)
@@@ -2429,7 -2211,6 +2428,7 @@@ static int write_packed_entry_fn(struc
        return 0;
  }
  
 +/* This should return a meaningful errno on failure */
  int lock_packed_refs(int flags)
  {
        struct packed_ref_cache *packed_ref_cache;
        return 0;
  }
  
 +/*
 + * Commit the packed refs changes.
 + * On error we must make sure that errno contains a meaningful value.
 + */
  int commit_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        int error = 0;
 +      int save_errno = 0;
  
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
        do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
                                 0, write_packed_entry_fn,
                                 &packed_ref_cache->lock->fd);
 -      if (commit_lock_file(packed_ref_cache->lock))
 +      if (commit_lock_file(packed_ref_cache->lock)) {
 +              save_errno = errno;
                error = -1;
 +      }
        packed_ref_cache->lock = NULL;
        release_packed_ref_cache(packed_ref_cache);
 +      errno = save_errno;
        return error;
  }
  
@@@ -2681,12 -2454,12 +2680,12 @@@ static int curate_packed_ref_fn(struct 
        return 0;
  }
  
 -int repack_without_refs(const char **refnames, int n)
 +int repack_without_refs(const char **refnames, int n, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
        struct string_list_item *ref_to_delete;
 -      int i, removed = 0;
 +      int i, ret, removed = 0;
  
        /* Look for a packed ref */
        for (i = 0; i < n; i++)
                return 0; /* no refname exists in packed refs */
  
        if (lock_packed_refs(0)) {
 +              if (err) {
 +                      unable_to_lock_message(git_path("packed-refs"), errno,
 +                                             err);
 +                      return -1;
 +              }
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refnames[i]);
        }
        }
  
        /* Write what remains */
 -      return commit_packed_refs();
 +      ret = commit_packed_refs();
 +      if (ret && err)
 +              strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
 +                          strerror(errno));
 +      return ret;
  }
  
  static int repack_without_ref(const char *refname)
  {
 -      return repack_without_refs(&refname, 1);
 +      return repack_without_refs(&refname, 1, NULL);
  }
  
  static int delete_ref_loose(struct ref_lock *lock, int flag)
@@@ -2976,7 -2740,6 +2975,7 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
 +/* This function must set a meaningful errno on failure */
  int log_ref_setup(const char *refname, char *logfile, int bufsize)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
             starts_with(refname, "refs/remotes/") ||
             starts_with(refname, "refs/notes/") ||
             !strcmp(refname, "HEAD"))) {
 -              if (safe_create_leading_directories(logfile) < 0)
 -                      return error("unable to create directory for %s",
 -                                   logfile);
 +              if (safe_create_leading_directories(logfile) < 0) {
 +                      int save_errno = errno;
 +                      error("unable to create directory for %s", logfile);
 +                      errno = save_errno;
 +                      return -1;
 +              }
                oflags |= O_CREAT;
        }
  
  
                if ((oflags & O_CREAT) && errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
 -                              return error("There are still logs under '%s'",
 -                                           logfile);
 +                              int save_errno = errno;
 +                              error("There are still logs under '%s'",
 +                                    logfile);
 +                              errno = save_errno;
 +                              return -1;
                        }
                        logfd = open(logfile, oflags, 0666);
                }
  
 -              if (logfd < 0)
 -                      return error("Unable to append to %s: %s",
 -                                   logfile, strerror(errno));
 +              if (logfd < 0) {
 +                      int save_errno = errno;
 +                      error("Unable to append to %s: %s", logfile,
 +                            strerror(errno));
 +                      errno = save_errno;
 +                      return -1;
 +              }
        }
  
        adjust_shared_perm(logfile);
@@@ -3058,38 -2811,24 +3057,38 @@@ static int log_ref_write(const char *re
                len += copy_msg(logrec + len - 1, msg) - 1;
        written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
        free(logrec);
 -      if (close(logfd) != 0 || written != len)
 -              return error("Unable to append to %s", log_file);
 +      if (written != len) {
 +              int save_errno = errno;
 +              close(logfd);
 +              error("Unable to append to %s", log_file);
 +              errno = save_errno;
 +              return -1;
 +      }
 +      if (close(logfd)) {
 +              int save_errno = errno;
 +              error("Unable to append to %s", log_file);
 +              errno = save_errno;
 +              return -1;
 +      }
        return 0;
  }
  
 -static int is_branch(const char *refname)
 +int is_branch(const char *refname)
  {
        return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
  }
  
 +/* This function must return a meaningful errno */
  int write_ref_sha1(struct ref_lock *lock,
        const unsigned char *sha1, const char *logmsg)
  {
        static char term = '\n';
        struct object *o;
  
 -      if (!lock)
 +      if (!lock) {
 +              errno = EINVAL;
                return -1;
 +      }
        if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
                unlock_ref(lock);
                return 0;
                error("Trying to write ref %s with nonexistent object %s",
                        lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
 +              errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
                error("Trying to write non-commit object %s to branch %s",
                        sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
 +              errno = EINVAL;
                return -1;
        }
        if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
 -          write_in_full(lock->lock_fd, &term, 1) != 1
 -              || close_ref(lock) < 0) {
 +          write_in_full(lock->lock_fd, &term, 1) != 1 ||
 +          close_ref(lock) < 0) {
 +              int save_errno = errno;
                error("Couldn't write %s", lock->lk->filename);
                unlock_ref(lock);
 +              errno = save_errno;
                return -1;
        }
        clear_loose_ref_cache(&ref_cache);
@@@ -3497,7 -3232,7 +3496,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) {
@@@ -3552,13 -3287,10 +3551,13 @@@ static struct ref_lock *update_ref_lock
  
  static int update_ref_write(const char *action, const char *refname,
                            const unsigned char *sha1, struct ref_lock *lock,
 -                          enum action_on_err onerr)
 +                          struct strbuf *err, enum action_on_err onerr)
  {
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
 +              if (err)
 +                      strbuf_addf(err, str, refname);
 +
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
                case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
@@@ -3601,13 -3333,10 +3600,13 @@@ struct ref_transaction *ref_transaction
        return xcalloc(1, sizeof(struct ref_transaction));
  }
  
 -static void ref_transaction_free(struct ref_transaction *transaction)
 +void ref_transaction_free(struct ref_transaction *transaction)
  {
        int i;
  
 +      if (!transaction)
 +              return;
 +
        for (i = 0; i < transaction->nr; i++)
                free(transaction->updates[i]);
  
        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)
  {
        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)
 +int ref_transaction_update(struct ref_transaction *transaction,
 +                         const char *refname,
 +                         const unsigned char *new_sha1,
 +                         const unsigned char *old_sha1,
 +                         int flags, int have_old,
 +                         struct strbuf *err)
  {
 -      struct ref_update *update = add_update(transaction, refname);
 +      struct ref_update *update;
  
 +      if (have_old && !old_sha1)
 +              die("BUG: have_old is true but old_sha1 is NULL");
 +
 +      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);
 +      return 0;
  }
  
  void ref_transaction_create(struct ref_transaction *transaction,
                            const char *refname,
 -                          unsigned char *new_sha1,
 +                          const unsigned char *new_sha1,
                            int flags)
  {
        struct ref_update *update = add_update(transaction, refname);
  
  void ref_transaction_delete(struct ref_transaction *transaction,
                            const char *refname,
 -                          unsigned char *old_sha1,
 +                          const unsigned char *old_sha1,
                            int flags, int have_old)
  {
        struct ref_update *update = add_update(transaction, refname);
@@@ -3685,7 -3412,7 +3684,7 @@@ int update_ref(const char *action, cons
        lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
        if (!lock)
                return 1;
 -      return update_ref_write(action, refname, sha1, lock, onerr);
 +      return update_ref_write(action, refname, sha1, lock, NULL, onerr);
  }
  
  static int ref_update_compare(const void *r1, const void *r2)
  }
  
  static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 -                                      enum action_on_err onerr)
 +                                      struct strbuf *err)
  {
        int i;
        for (i = 1; i < n; i++)
                if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        const char *str =
                                "Multiple updates for ref '%s' not allowed.";
 -                      switch (onerr) {
 -                      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;
 -                      }
 +                      if (err)
 +                              strbuf_addf(err, str, updates[i]->refname);
 +
                        return 1;
                }
        return 0;
  }
  
  int ref_transaction_commit(struct ref_transaction *transaction,
 -                         const char *msg, enum action_on_err onerr)
 +                         const char *msg, struct strbuf *err)
  {
        int ret = 0, delnum = 0, i;
        const char **delnames;
  
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
 -      ret = ref_update_reject_duplicates(updates, n, onerr);
 +      ret = ref_update_reject_duplicates(updates, n, err);
        if (ret)
                goto cleanup;
  
                                               (update->have_old ?
                                                update->old_sha1 : NULL),
                                               update->flags,
 -                                             &update->type, onerr);
 +                                             &update->type,
 +                                             UPDATE_REFS_QUIET_ON_ERR);
                if (!update->lock) {
 +                      if (err)
 +                              strbuf_addf(err, "Cannot lock the ref '%s'.",
 +                                          update->refname);
                        ret = 1;
                        goto cleanup;
                }
                        ret = update_ref_write(msg,
                                               update->refname,
                                               update->new_sha1,
 -                                             update->lock, onerr);
 +                                             update->lock, err,
 +                                             UPDATE_REFS_QUIET_ON_ERR);
                        update->lock = NULL; /* freed by update_ref_write */
                        if (ret)
                                goto cleanup;
                }
        }
  
 -      ret |= repack_without_refs(delnames, delnum);
 +      ret |= repack_without_refs(delnames, delnum, err);
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
        clear_loose_ref_cache(&ref_cache);
@@@ -3786,6 -3513,7 +3785,6 @@@ cleanup
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
        free(delnames);
 -      ref_transaction_free(transaction);
        return ret;
  }