Merge branch 'rs/ref-transaction-0'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 18:18:37 +0000 (11:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 18:18:37 +0000 (11:18 -0700)
Early part of the "ref transaction" topic.

* rs/ref-transaction-0:
refs.c: change ref_transaction_update() to do error checking and return status
refs.c: remove the onerr argument to ref_transaction_commit
update-ref: use err argument to get error from ref_transaction_commit
refs.c: make update_ref_write update a strbuf on failure
refs.c: make ref_update_reject_duplicates take a strbuf argument for errors
refs.c: log_ref_write should try to return meaningful errno
refs.c: make resolve_ref_unsafe set errno to something meaningful on error
refs.c: commit_packed_refs to return a meaningful errno on failure
refs.c: make remove_empty_directories always set errno to something sane
refs.c: verify_lock should set errno to something meaningful
refs.c: make sure log_ref_setup returns a meaningful errno
refs.c: add an err argument to repack_without_refs
lockfile.c: make lock_file return a meaningful errno on failurei
lockfile.c: add a new public function unable_to_lock_message
refs.c: add a strbuf argument to ref_transaction_commit for error logging
refs.c: allow passing NULL to ref_transaction_free
refs.c: constify the sha arguments for ref_transaction_create|delete|update
refs.c: ref_transaction_commit should not free the transaction
refs.c: remove ref_transaction_rollback

1  2 
builtin/remote.c
cache.h
lockfile.c
refs.c
diff --combined builtin/remote.c
index 8e1dc39162681503505c9c3897e046514ce2e6a8,401feb39704dcaf9125ec174382be3457ee2d926..9a4640dbf0150bff38efcbb0126abdf1aeae7ed4
@@@ -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,17 -265,16 +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;
@@@ -754,7 -755,7 +754,7 @@@ static int remove_branches(struct strin
        branch_names = xmalloc(branches->nr * sizeof(*branch_names));
        for (i = 0; i < branches->nr; i++)
                branch_names[i] = branches->items[i].string;
-       result |= repack_without_refs(branch_names, branches->nr);
+       result |= repack_without_refs(branch_names, branches->nr, NULL);
        free(branch_names);
  
        for (i = 0; i < branches->nr; i++) {
@@@ -1332,7 -1333,8 +1332,8 @@@ static int prune_remote(const char *rem
                for (i = 0; i < states.stale.nr; i++)
                        delete_refs[i] = states.stale.items[i].util;
                if (!dry_run)
-                       result |= repack_without_refs(delete_refs, states.stale.nr);
+                       result |= repack_without_refs(delete_refs,
+                                                     states.stale.nr, NULL);
                free(delete_refs);
        }
  
diff --combined cache.h
index 92fc9f1121776fbebf180b5e9d958982e8a98014,e7ec6267bd70ff345bbba684a2b0ab067c923de3..6e9a0a6d0cf9f2ee076f1bc490eb2e2fc52cbcaa
+++ b/cache.h
@@@ -150,7 -150,6 +150,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
   */
@@@ -287,22 -283,12 +287,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;
@@@ -331,6 -317,7 +331,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))
@@@ -485,17 -472,12 +485,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);
@@@ -507,7 -489,6 +507,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);
@@@ -578,6 -559,8 +578,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);
@@@ -585,6 -568,7 +587,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 *);
@@@ -995,7 -979,7 +997,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);
@@@ -1017,7 -1001,7 +1019,7 @@@ extern int validate_headref(const char 
  
  extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
  extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 -extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
 +extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
  extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
  
  extern void *read_object_with_reference(const unsigned char *sha1,
@@@ -1096,7 -1080,6 +1098,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);
diff --combined lockfile.c
index b7066143494bcfa3e629dd718daf8959af6c4b86,a921d77afcc5b54fdeba7bca6afcd85548332f0d..2564a7f5447b904585f629f0d1233c8a59483a40
@@@ -5,6 -5,7 +5,6 @@@
  #include "sigchain.h"
  
  static struct lock_file *lock_file_list;
 -static const char *alternate_index_output;
  
  static void remove_lock_file(void)
  {
@@@ -120,7 -121,7 +120,7 @@@ static char *resolve_symlink(char *p, s
        return p;
  }
  
+ /* Make sure errno contains a meaningful value on error */
  static int lock_file(struct lock_file *lk, const char *path, int flags)
  {
        /*
         */
        static const size_t max_path_len = sizeof(lk->filename) - 5;
  
-       if (strlen(path) >= max_path_len)
+       if (strlen(path) >= max_path_len) {
+               errno = ENAMETOOLONG;
                return -1;
+       }
        strcpy(lk->filename, path);
        if (!(flags & LOCK_NODEREF))
                resolve_symlink(lk->filename, max_path_len);
                        lock_file_list = lk;
                        lk->on_list = 1;
                }
-               if (adjust_shared_perm(lk->filename))
-                       return error("cannot fix permission bits on %s",
-                                    lk->filename);
+               if (adjust_shared_perm(lk->filename)) {
+                       int save_errno = errno;
+                       error("cannot fix permission bits on %s",
+                             lk->filename);
+                       errno = save_errno;
+                       return -1;
+               }
        }
        else
                lk->filename[0] = 0;
        return lk->fd;
  }
  
static char *unable_to_lock_message(const char *path, int err)
void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
  {
-       struct strbuf buf = STRBUF_INIT;
        if (err == EEXIST) {
-               strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n"
+               strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
                    "If no other git process is currently running, this probably means a\n"
                    "git process crashed in this repository earlier. Make sure no other git\n"
                    "process is running and remove the file manually to continue.",
                            absolute_path(path), strerror(err));
        } else
-               strbuf_addf(&buf, "Unable to create '%s.lock': %s",
+               strbuf_addf(buf, "Unable to create '%s.lock': %s",
                            absolute_path(path), strerror(err));
-       return strbuf_detach(&buf, NULL);
  }
  
  int unable_to_lock_error(const char *path, int err)
  {
-       char *msg = unable_to_lock_message(path, err);
-       error("%s", msg);
-       free(msg);
+       struct strbuf buf = STRBUF_INIT;
+       unable_to_lock_message(path, err, &buf);
+       error("%s", buf.buf);
+       strbuf_release(&buf);
        return -1;
  }
  
  NORETURN void unable_to_lock_index_die(const char *path, int err)
  {
-       die("%s", unable_to_lock_message(path, err));
+       struct strbuf buf = STRBUF_INIT;
+       unable_to_lock_message(path, err, &buf);
+       die("%s", buf.buf);
  }
  
+ /* This should return a meaningful errno on failure */
  int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
  {
        int fd = lock_file(lk, path, flags);
@@@ -251,6 -261,25 +260,6 @@@ int hold_locked_index(struct lock_file 
                                         : 0);
  }
  
 -void set_alternate_index_output(const char *name)
 -{
 -      alternate_index_output = name;
 -}
 -
 -int commit_locked_index(struct lock_file *lk)
 -{
 -      if (alternate_index_output) {
 -              if (lk->fd >= 0 && close_lock_file(lk))
 -                      return -1;
 -              if (rename(lk->filename, alternate_index_output))
 -                      return -1;
 -              lk->filename[0] = 0;
 -              return 0;
 -      }
 -      else
 -              return commit_lock_file(lk);
 -}
 -
  void rollback_lock_file(struct lock_file *lk)
  {
        if (lk->filename[0]) {
diff --combined refs.c
index 061d233e4b6404c57ff8d62715758b900d02e051,3f05e88329216ab4849b54df3c3e034dcf173ff3..dc761599fde96cbf4ddd88608ffc13bce4bf0040
--- 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,6 -1334,7 +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);
@@@ -2131,18 -1943,22 +2142,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;
@@@ -2155,14 -1971,16 +2170,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;
  }
@@@ -2251,6 -2069,7 +2268,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)
@@@ -2411,6 -2230,7 +2429,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;
  }
  
@@@ -2654,12 -2482,12 +2681,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)
@@@ -2940,6 -2777,7 +2976,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);
@@@ -3011,8 -2859,19 +3058,19 @@@ 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;
  }
  
@@@ -3021,14 -2880,17 +3079,17 @@@ static int is_branch(const char *refnam
        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);
@@@ -3432,7 -3298,7 +3497,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) {
@@@ -3487,10 -3353,13 +3552,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;
@@@ -3533,10 -3402,13 +3601,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);
@@@ -3612,7 -3486,7 +3685,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);
@@@ -3713,7 -3587,6 +3786,6 @@@ cleanup
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
        free(delnames);
-       ref_transaction_free(transaction);
        return ret;
  }