Merge branch 'jk/blame-commit-label'
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 Feb 2015 21:39:50 +0000 (13:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 Feb 2015 21:39:50 +0000 (13:39 -0800)
"git blame HEAD -- missing" failed to correctly say "HEAD" when it
tried to say "No such path 'missing' in HEAD".

* jk/blame-commit-label:
blame.c: fix garbled error message
use xstrdup_or_null to replace ternary conditionals
builtin/commit.c: use xstrdup_or_null instead of envdup
builtin/apply.c: use xstrdup_or_null instead of null_strdup
git-compat-util: add xstrdup_or_null helper

1  2 
builtin/apply.c
builtin/blame.c
builtin/commit.c
config.c
git-compat-util.h
grep.c
notes.c
refs.c
remote.c
shallow.c
walker.c
diff --combined builtin/apply.c
index 0aad91283959bce51aba596c6f3d2d0925e5ba92,4bb31fd6ae1fcee8fa901a289dda1cfcb95aeb52..dfd7a34117c5d2a9287f59626941a216fc00ff90
@@@ -7,7 -7,6 +7,7 @@@
   *
   */
  #include "cache.h"
 +#include "lockfile.h"
  #include "cache-tree.h"
  #include "quote.h"
  #include "blob.h"
@@@ -436,7 -435,7 +436,7 @@@ static unsigned long linelen(const cha
  
  static int is_dev_null(const char *str)
  {
 -      return !memcmp("/dev/null", str, 9) && isspace(str[9]);
 +      return skip_prefix(str, "/dev/null", &str) && isspace(*str);
  }
  
  #define TERM_SPACE    1
@@@ -657,11 -656,6 +657,6 @@@ static size_t diff_timestamp_len(const 
        return line + len - end;
  }
  
- static char *null_strdup(const char *s)
- {
-       return s ? xstrdup(s) : NULL;
- }
  static char *find_name_common(const char *line, const char *def,
                              int p_value, const char *end, int terminate)
  {
                        start = line;
        }
        if (!start)
-               return squash_slash(null_strdup(def));
+               return squash_slash(xstrdup_or_null(def));
        len = line - start;
        if (!len)
-               return squash_slash(null_strdup(def));
+               return squash_slash(xstrdup_or_null(def));
  
        /*
         * Generally we prefer the shorter name, especially
@@@ -909,7 -903,7 +904,7 @@@ static void parse_traditional_patch(con
                        patch->old_name = name;
                } else {
                        patch->old_name = name;
-                       patch->new_name = null_strdup(name);
+                       patch->new_name = xstrdup_or_null(name);
                }
        }
        if (!name)
@@@ -998,7 -992,7 +993,7 @@@ static int gitdiff_delete(const char *l
  {
        patch->is_delete = 1;
        free(patch->old_name);
-       patch->old_name = null_strdup(patch->def_name);
+       patch->old_name = xstrdup_or_null(patch->def_name);
        return gitdiff_oldmode(line, patch);
  }
  
@@@ -1006,7 -1000,7 +1001,7 @@@ static int gitdiff_newfile(const char *
  {
        patch->is_new = 1;
        free(patch->new_name);
-       patch->new_name = null_strdup(patch->def_name);
+       patch->new_name = xstrdup_or_null(patch->def_name);
        return gitdiff_newmode(line, patch);
  }
  
@@@ -3728,7 -3722,7 +3723,7 @@@ static void build_fake_ancestor(struct 
                        if (!preimage_sha1_in_gitlink_patch(patch, sha1))
                                ; /* ok, the textual part looks sane */
                        else
 -                              die("sha1 information is lacking or useless for submoule %s",
 +                              die("sha1 information is lacking or useless for submodule %s",
                                    name);
                } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
                        ; /* ok */
@@@ -4180,7 -4174,7 +4175,7 @@@ static int write_out_results(struct pat
        if (cpath.nr) {
                struct string_list_item *item;
  
 -              sort_string_list(&cpath);
 +              string_list_sort(&cpath);
                for_each_string_list_item(item, &cpath)
                        fprintf(stderr, "U %s\n", item->string);
                string_list_clear(&cpath, 0);
diff --combined builtin/blame.c
index 303e217ae919f21aa4d4574bd1720b5f4d635c32,699109b4a9d7663c0f4eebb82e349e3778c2730d..0374fe8056acfe193d2cbd5c553b3de889b788fe
@@@ -2286,7 -2286,7 +2286,7 @@@ static struct commit *fake_working_tree
        commit->date = now;
        parent_tail = &commit->parents;
  
 -      if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
                die("no such ref: HEAD");
  
        parent_tail = append_parent(parent_tail, head_sha1);
        return commit;
  }
  
- static const char *prepare_final(struct scoreboard *sb)
+ static char *prepare_final(struct scoreboard *sb)
  {
        int i;
        const char *final_commit_name = NULL;
                sb->final = (struct commit *) obj;
                final_commit_name = revs->pending.objects[i].name;
        }
-       return final_commit_name;
+       return xstrdup_or_null(final_commit_name);
  }
  
- static const char *prepare_initial(struct scoreboard *sb)
+ static char *prepare_initial(struct scoreboard *sb)
  {
        int i;
        const char *final_commit_name = NULL;
        }
        if (!final_commit_name)
                die("No commit to dig down to?");
-       return final_commit_name;
+       return xstrdup(final_commit_name);
  }
  
  static int blame_copy_callback(const struct option *option, const char *arg, int unset)
@@@ -2489,7 -2489,7 +2489,7 @@@ int cmd_blame(int argc, const char **ar
        struct origin *o;
        struct blame_entry *ent = NULL;
        long dashdash_pos, lno;
-       const char *final_commit_name = NULL;
+       char *final_commit_name = NULL;
        enum object_type type;
  
        static struct string_list range_list;
@@@ -2786,6 -2786,8 +2786,8 @@@ parse_done
  
        assign_blame(&sb, opt);
  
+       free(final_commit_name);
        if (incremental)
                return 0;
  
diff --combined builtin/commit.c
index 7d90c3591567d10f9a075ec38048b93923f707d4,b1cf2842408043eec81900bb886552f7fa4f08cc..5cd1478ebfdae36a8112c7f93f9017b54954cf44
@@@ -6,7 -6,6 +6,7 @@@
   */
  
  #include "cache.h"
 +#include "lockfile.h"
  #include "cache-tree.h"
  #include "color.h"
  #include "dir.h"
@@@ -316,8 -315,8 +316,8 @@@ static void refresh_cache_or_die(int re
                die_resolve_conflict("commit");
  }
  
 -static char *prepare_index(int argc, const char **argv, const char *prefix,
 -                         const struct commit *current_head, int is_status)
 +static const char *prepare_index(int argc, const char **argv, const char *prefix,
 +                               const struct commit *current_head, int is_status)
  {
        struct string_list partial;
        struct pathspec pathspec;
                        die(_("unable to create temporary index"));
  
                old_index_env = getenv(INDEX_ENVIRONMENT);
 -              setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
 +              setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
  
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
                        die(_("interactive add failed"));
                        unsetenv(INDEX_ENVIRONMENT);
  
                discard_cache();
 -              read_cache_from(index_lock.filename);
 +              read_cache_from(index_lock.filename.buf);
                if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
                        warning(_("Failed to update main cache tree"));
  
                commit_style = COMMIT_NORMAL;
 -              return index_lock.filename;
 +              return index_lock.filename.buf;
        }
  
        /*
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
 -              return index_lock.filename;
 +              return index_lock.filename.buf;
        }
  
        /*
                die(_("unable to write temporary index file"));
  
        discard_cache();
 -      read_cache_from(false_lock.filename);
 +      read_cache_from(false_lock.filename.buf);
  
 -      return false_lock.filename;
 +      return false_lock.filename.buf;
  }
  
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
@@@ -522,12 -521,6 +522,12 @@@ static int is_a_merge(const struct comm
        return !!(current_head->parents && current_head->parents->next);
  }
  
 +static void assert_split_ident(struct ident_split *id, const struct strbuf *buf)
 +{
 +      if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin)
 +              die("BUG: unable to parse our own ident: %s", buf->buf);
 +}
 +
  static void export_one(const char *var, const char *s, const char *e, int hack)
  {
        struct strbuf buf = STRBUF_INIT;
        strbuf_release(&buf);
  }
  
 -static int sane_ident_split(struct ident_split *person)
 -{
 -      if (!person->name_begin || !person->name_end ||
 -          person->name_begin == person->name_end)
 -              return 0; /* no human readable name */
 -      if (!person->mail_begin || !person->mail_end ||
 -          person->mail_begin == person->mail_end)
 -              return 0; /* no usable mail */
 -      if (!person->date_begin || !person->date_end ||
 -          !person->tz_begin || !person->tz_end)
 -              return 0;
 -      return 1;
 -}
 -
  static int parse_force_date(const char *in, struct strbuf *out)
  {
        strbuf_addch(out, '@');
@@@ -559,20 -566,14 +559,14 @@@ static void set_ident_var(char **buf, c
        *buf = val;
  }
  
- static char *envdup(const char *var)
- {
-       const char *val = getenv(var);
-       return val ? xstrdup(val) : NULL;
- }
  static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
        struct ident_split author;
  
-       name = envdup("GIT_AUTHOR_NAME");
-       email = envdup("GIT_AUTHOR_EMAIL");
-       date = envdup("GIT_AUTHOR_DATE");
+       name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME"));
+       email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL"));
+       date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE"));
  
        if (author_message) {
                struct ident_split ident;
        }
  
        strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
 -      if (!split_ident_line(&author, author_ident->buf, author_ident->len) &&
 -          sane_ident_split(&author)) {
 -              export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
 -              export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
 -              export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
 -      }
 -
 +      assert_split_ident(&author, author_ident);
 +      export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
 +      export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
 +      export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
        free(name);
        free(email);
        free(date);
  }
  
 -static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
 -{
 -      if (split_ident_line(id, buf->buf, buf->len) ||
 -          !sane_ident_split(id))
 -              die(_("Malformed ident string: '%s'"), buf->buf);
 -}
 -
  static int author_date_is_interesting(void)
  {
        return author_message || force_date;
@@@ -782,8 -793,32 +776,8 @@@ static int prepare_to_commit(const cha
        if (clean_message_contents)
                stripspace(&sb, 0);
  
 -      if (signoff) {
 -              /*
 -               * See if we have a Conflicts: block at the end. If yes, count
 -               * its size, so we can ignore it.
 -               */
 -              int ignore_footer = 0;
 -              int i, eol, previous = 0;
 -              const char *nl;
 -
 -              for (i = 0; i < sb.len; i++) {
 -                      nl = memchr(sb.buf + i, '\n', sb.len - i);
 -                      if (nl)
 -                              eol = nl - sb.buf;
 -                      else
 -                              eol = sb.len;
 -                      if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
 -                              ignore_footer = sb.len - previous;
 -                              break;
 -                      }
 -                      while (i < eol)
 -                              i++;
 -                      previous = eol;
 -              }
 -
 -              append_signoff(&sb, ignore_footer, 0);
 -      }
 +      if (signoff)
 +              append_signoff(&sb, ignore_non_trailer(&sb), 0);
  
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                        "%s", only_include_assumed);
  
 -              split_ident_or_die(&ai, author_ident);
 -              split_ident_or_die(&ci, &committer_ident);
 +              /*
 +               * These should never fail because they come from our own
 +               * fmt_ident. They may fail the sane_ident test, but we know
 +               * that the name and mail pointers will at least be valid,
 +               * which is enough for our tests and printing here.
 +               */
 +              assert_split_ident(&ai, author_ident);
 +              assert_split_ident(&ci, &committer_ident);
  
                if (ident_cmp(&ai, &ci))
                        status_printf_ln(s, GIT_COLOR_NORMAL,
@@@ -1236,21 -1265,22 +1230,21 @@@ static int dry_run_commit(int argc, con
        return commitable ? 0 : 1;
  }
  
 -static int parse_status_slot(const char *var, int offset)
 +static int parse_status_slot(const char *slot)
  {
 -      if (!strcasecmp(var+offset, "header"))
 +      if (!strcasecmp(slot, "header"))
                return WT_STATUS_HEADER;
 -      if (!strcasecmp(var+offset, "branch"))
 +      if (!strcasecmp(slot, "branch"))
                return WT_STATUS_ONBRANCH;
 -      if (!strcasecmp(var+offset, "updated")
 -              || !strcasecmp(var+offset, "added"))
 +      if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
                return WT_STATUS_UPDATED;
 -      if (!strcasecmp(var+offset, "changed"))
 +      if (!strcasecmp(slot, "changed"))
                return WT_STATUS_CHANGED;
 -      if (!strcasecmp(var+offset, "untracked"))
 +      if (!strcasecmp(slot, "untracked"))
                return WT_STATUS_UNTRACKED;
 -      if (!strcasecmp(var+offset, "nobranch"))
 +      if (!strcasecmp(slot, "nobranch"))
                return WT_STATUS_NOBRANCH;
 -      if (!strcasecmp(var+offset, "unmerged"))
 +      if (!strcasecmp(slot, "unmerged"))
                return WT_STATUS_UNMERGED;
        return -1;
  }
  static int git_status_config(const char *k, const char *v, void *cb)
  {
        struct wt_status *s = cb;
 +      const char *slot_name;
  
        if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
 -      if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
 -              int slot = parse_status_slot(k, 13);
 +      if (skip_prefix(k, "status.color.", &slot_name) ||
 +          skip_prefix(k, "color.status.", &slot_name)) {
 +              int slot = parse_status_slot(slot_name);
                if (slot < 0)
                        return 0;
                if (!v)
                        return config_error_nonbool(k);
 -              color_parse(v, k, s->color_palette[slot]);
 -              return 0;
 +              return color_parse(v, s->color_palette[slot]);
        }
        if (!strcmp(k, "status.relativepaths")) {
                s->relative_paths = git_config_bool(k, v);
@@@ -1477,12 -1506,14 +1471,12 @@@ static void print_summary(const char *p
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
 -      head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
 -      printf("[%s%s ",
 -              starts_with(head, "refs/heads/") ?
 -                      head + 11 :
 -                      !strcmp(head, "HEAD") ?
 -                              _("detached HEAD") :
 -                              head,
 -              initial_commit ? _(" (root-commit)") : "");
 +      head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
 +      if (!strcmp(head, "HEAD"))
 +              head = _("detached HEAD");
 +      else
 +              skip_prefix(head, "refs/heads/", &head);
 +      printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
  
        if (!log_tree_commit(&rev, commit)) {
                rev.always_show_header = 1;
@@@ -1773,8 -1804,8 +1767,8 @@@ int cmd_commit(int argc, const char **a
            ref_transaction_update(transaction, "HEAD", sha1,
                                   current_head
                                   ? current_head->object.sha1 : NULL,
 -                                 0, !!current_head, &err) ||
 -          ref_transaction_commit(transaction, sb.buf, &err)) {
 +                                 0, !!current_head, sb.buf, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
                rollback_index_files();
                die("%s", err.buf);
        }
diff --combined config.c
index 752e2e227f56edfb7b2ad168798c110c14a2c5e8,400d2e47de5bdab60291ca38e3151745ad35c488..e5e64dc60fa4890a99b556c31a8665b7c92375d3
+++ b/config.c
@@@ -6,7 -6,6 +6,7 @@@
   *
   */
  #include "cache.h"
 +#include "lockfile.h"
  #include "exec_cmd.h"
  #include "strbuf.h"
  #include "quote.h"
@@@ -506,9 -505,9 +506,9 @@@ static int git_parse_signed(const char 
                        errno = EINVAL;
                        return 0;
                }
 -              uval = abs(val);
 +              uval = labs(val);
                uval *= factor;
 -              if (uval > max || abs(val) > uval) {
 +              if (uval > max || labs(val) > uval) {
                        errno = ERANGE;
                        return 0;
                }
@@@ -896,16 -895,6 +896,16 @@@ static int git_default_core_config(cons
                return 0;
        }
  
 +      if (!strcmp(var, "core.protecthfs")) {
 +              protect_hfs = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "core.protectntfs")) {
 +              protect_ntfs = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -1340,7 -1329,7 +1340,7 @@@ static int configset_add_value(struct c
                string_list_init(&e->value_list, 1);
                hashmap_add(&cs->config_hash, e);
        }
-       si = string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL);
+       si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
  
        ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
        l_item = &cs->list.items[cs->list.nr++];
@@@ -2051,9 -2040,9 +2051,9 @@@ int git_config_set_multivar_in_file(con
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
  
 -              if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +              if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                        error("chmod on %s failed: %s",
 -                              lock->filename, strerror(errno));
 +                              lock->filename.buf, strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
 +              lock = NULL;
                goto out_free;
        }
  
@@@ -2133,7 -2121,7 +2133,7 @@@ out_free
        return ret;
  
  write_err_out:
 -      ret = write_error(lock->filename);
 +      ret = write_error(lock->filename.buf);
        goto out_free;
  
  }
@@@ -2234,9 -2222,9 +2234,9 @@@ int git_config_rename_section_in_file(c
  
        fstat(fileno(config_file), &st);
  
 -      if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +      if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                ret = error("chmod on %s failed: %s",
 -                              lock->filename, strerror(errno));
 +                              lock->filename.buf, strerror(errno));
                goto out;
        }
  
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
 -                                      ret = write_error(lock->filename);
 +                                      ret = write_error(lock->filename.buf);
                                        goto out;
                                }
                                /*
                        continue;
                length = strlen(output);
                if (write_in_full(out_fd, output, length) != length) {
 -                      ret = write_error(lock->filename);
 +                      ret = write_error(lock->filename.buf);
                        goto out;
                }
        }
diff --combined git-compat-util.h
index eb9b0ff32829ef62bf2d1af432742841b9720906,98cb78edf7b982e6083a5c72030e6dea0381deb5..553fc017623be771878778c84aca08a4684f6902
@@@ -75,8 -75,7 +75,8 @@@
  # endif
  #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
        !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
 -      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
 +      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
 +      !defined(__CYGWIN__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
  #endif
@@@ -212,15 -211,8 +212,15 @@@ extern char *gitbasename(char *)
  #endif
  
  #ifndef NO_OPENSSL
 +#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0
 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
  #include <openssl/ssl.h>
  #include <openssl/err.h>
 +#undef MAC_OS_X_VERSION_MIN_REQUIRED
 +#undef __AVAILABILITY_MACROS_USES_AVAILABILITY
 +#ifdef NO_HMAC_CTX_CLEANUP
 +#define HMAC_CTX_cleanup HMAC_cleanup
 +#endif
  #endif
  
  /* On most systems <netdb.h> would have given us this, but
@@@ -334,8 -326,6 +334,8 @@@ static inline char *git_find_last_dir_s
  
  #include "wildmatch.h"
  
 +struct strbuf;
 +
  /* General helper functions */
  extern void vreportf(const char *prefix, const char *err, va_list params);
  extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
@@@ -482,40 -472,6 +482,40 @@@ extern int git_munmap(void *start, size
  #define on_disk_bytes(st) ((st).st_blocks * 512)
  #endif
  
 +#ifdef NEEDS_MODE_TRANSLATION
 +#undef S_IFMT
 +#undef S_IFREG
 +#undef S_IFDIR
 +#undef S_IFLNK
 +#undef S_IFBLK
 +#undef S_IFCHR
 +#undef S_IFIFO
 +#undef S_IFSOCK
 +#define S_IFMT   0170000
 +#define S_IFREG  0100000
 +#define S_IFDIR  0040000
 +#define S_IFLNK  0120000
 +#define S_IFBLK  0060000
 +#define S_IFCHR  0020000
 +#define S_IFIFO  0010000
 +#define S_IFSOCK 0140000
 +#ifdef stat
 +#undef stat
 +#endif
 +#define stat(path, buf) git_stat(path, buf)
 +extern int git_stat(const char *, struct stat *);
 +#ifdef fstat
 +#undef fstat
 +#endif
 +#define fstat(fd, buf) git_fstat(fd, buf)
 +extern int git_fstat(int, struct stat *);
 +#ifdef lstat
 +#undef lstat
 +#endif
 +#define lstat(path, buf) git_lstat(path, buf)
 +extern int git_lstat(const char *, struct stat *);
 +#endif
 +
  #define DEFAULT_PACKED_GIT_LIMIT \
        ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
  
@@@ -638,11 -594,6 +638,11 @@@ int inet_pton(int af, const char *src, 
  const char *inet_ntop(int af, const void *src, char *dst, size_t size);
  #endif
  
 +#ifdef NO_PTHREADS
 +#define atexit git_atexit
 +extern int git_atexit(void (*handler)(void));
 +#endif
 +
  extern void release_pack_memory(size_t);
  
  typedef void (*try_to_free_t)(size_t);
@@@ -678,6 -629,11 +678,11 @@@ extern char *xgetcwd(void)
  
  #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x)))
  
+ static inline char *xstrdup_or_null(const char *str)
+ {
+       return str ? xstrdup(str) : NULL;
+ }
  static inline size_t xsize_t(off_t len)
  {
        if (len > (size_t) len)
@@@ -820,27 -776,17 +825,27 @@@ void git_qsort(void *base, size_t nmemb
  #endif
  #endif
  
 -#if defined(__GNUC__) || (_MSC_VER >= 1400)
 +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__C99_MACRO_WITH_VA_ARGS)
  #define HAVE_VARIADIC_MACROS 1
  #endif
  
  /*
   * Preserves errno, prints a message, but gives no warning for ENOENT.
 - * Always returns the return value of unlink(2).
 + * Returns 0 on success, which includes trying to unlink an object that does
 + * not exist.
   */
  int unlink_or_warn(const char *path);
 + /*
 +  * Tries to unlink file.  Returns 0 if unlink succeeded
 +  * or the file already didn't exist.  Returns -1 and
 +  * appends a message to err suitable for
 +  * 'error("%s", err->buf)' on error.
 +  */
 +int unlink_or_msg(const char *file, struct strbuf *err);
  /*
 - * Likewise for rmdir(2).
 + * Preserves errno, prints a message, but gives no warning for ENOENT.
 + * Returns 0 on success, which includes trying to remove a directory that does
 + * not exist.
   */
  int rmdir_or_warn(const char *path);
  /*
diff --combined grep.c
index 6e085f829731146c319fff57da0ae482de97ff77,f48a648a0d673e5acecef19288b608cc0049659d..b58c7c64342698737f9c11b20457bd30ca1fc727
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -35,8 -35,7 +35,8 @@@ void init_grep_defaults(void
        strcpy(opt->color_filename, "");
        strcpy(opt->color_function, "");
        strcpy(opt->color_lineno, "");
 -      strcpy(opt->color_match, GIT_COLOR_BOLD_RED);
 +      strcpy(opt->color_match_context, GIT_COLOR_BOLD_RED);
 +      strcpy(opt->color_match_selected, GIT_COLOR_BOLD_RED);
        strcpy(opt->color_selected, "");
        strcpy(opt->color_sep, GIT_COLOR_CYAN);
        opt->color = -1;
@@@ -102,27 -101,17 +102,27 @@@ int grep_config(const char *var, const 
                color = opt->color_function;
        else if (!strcmp(var, "color.grep.linenumber"))
                color = opt->color_lineno;
 -      else if (!strcmp(var, "color.grep.match"))
 -              color = opt->color_match;
 +      else if (!strcmp(var, "color.grep.matchcontext"))
 +              color = opt->color_match_context;
 +      else if (!strcmp(var, "color.grep.matchselected"))
 +              color = opt->color_match_selected;
        else if (!strcmp(var, "color.grep.selected"))
                color = opt->color_selected;
        else if (!strcmp(var, "color.grep.separator"))
                color = opt->color_sep;
 +      else if (!strcmp(var, "color.grep.match")) {
 +              int rc = 0;
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              rc |= color_parse(value, opt->color_match_context);
 +              rc |= color_parse(value, opt->color_match_selected);
 +              return rc;
 +      }
  
        if (color) {
                if (!value)
                        return config_error_nonbool(var);
 -              color_parse(value, var, color);
 +              return color_parse(value, color);
        }
        return 0;
  }
@@@ -155,8 -144,7 +155,8 @@@ void grep_init(struct grep_opt *opt, co
        strcpy(opt->color_filename, def->color_filename);
        strcpy(opt->color_function, def->color_function);
        strcpy(opt->color_lineno, def->color_lineno);
 -      strcpy(opt->color_match, def->color_match);
 +      strcpy(opt->color_match_context, def->color_match_context);
 +      strcpy(opt->color_match_selected, def->color_match_selected);
        strcpy(opt->color_selected, def->color_selected);
        strcpy(opt->color_sep, def->color_sep);
  }
@@@ -1096,7 -1084,7 +1096,7 @@@ static void show_line(struct grep_opt *
                      const char *name, unsigned lno, char sign)
  {
        int rest = eol - bol;
 -      char *line_color = NULL;
 +      const char *match_color, *line_color = NULL;
  
        if (opt->file_break && opt->last_shown == 0) {
                if (opt->show_hunk_mark)
                int ch = *eol;
                int eflags = 0;
  
 +              if (sign == ':')
 +                      match_color = opt->color_match_selected;
 +              else
 +                      match_color = opt->color_match_context;
                if (sign == ':')
                        line_color = opt->color_selected;
                else if (sign == '-')
  
                        output_color(opt, bol, match.rm_so, line_color);
                        output_color(opt, bol + match.rm_so,
 -                                   match.rm_eo - match.rm_so,
 -                                   opt->color_match);
 +                                   match.rm_eo - match.rm_so, match_color);
                        bol += match.rm_eo;
                        rest -= match.rm_eo;
                        eflags = REG_NOTBOL;
@@@ -1661,8 -1646,8 +1661,8 @@@ void grep_source_init(struct grep_sourc
                      const void *identifier)
  {
        gs->type = type;
-       gs->name = name ? xstrdup(name) : NULL;
-       gs->path = path ? xstrdup(path) : NULL;
+       gs->name = xstrdup_or_null(name);
+       gs->path = xstrdup_or_null(path);
        gs->buf = NULL;
        gs->size = 0;
        gs->driver = NULL;
diff --combined notes.c
index c763a21eef5b64c984c22d18cd5f21c1bd32e5a3,ee5f0e71f38b31eed7e1c21618d13ca5219b1725..2be4d7f3fd081476001212b103f011e45b4a9c41
+++ b/notes.c
@@@ -902,7 -902,7 +902,7 @@@ int combine_notes_cat_sort_uniq(unsigne
        if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
                goto out;
        string_list_remove_empty_items(&sort_uniq_list, 0);
 -      sort_string_list(&sort_uniq_list);
 +      string_list_sort(&sort_uniq_list);
        string_list_remove_duplicates(&sort_uniq_list, 0);
  
        /* create a new blob object from sort_uniq_list */
@@@ -1006,7 -1006,7 +1006,7 @@@ void init_notes(struct notes_tree *t, c
        t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node));
        t->first_non_note = NULL;
        t->prev_non_note = NULL;
-       t->ref = notes_ref ? xstrdup(notes_ref) : NULL;
+       t->ref = xstrdup_or_null(notes_ref);
        t->combine_notes = combine_notes;
        t->initialized = 1;
        t->dirty = 0;
@@@ -1218,7 -1218,8 +1218,7 @@@ static void format_note(struct notes_tr
        if (!sha1)
                return;
  
 -      if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
 -                      type != OBJ_BLOB) {
 +      if (!(msg = read_sha1_file(sha1, &type, &msglen)) || type != OBJ_BLOB) {
                free(msg);
                return;
        }
diff --combined refs.c
index ed3b2cb405cc576f16e5b94d83683953b94e1e89,32dce4e41c377cbafbf9ede5f31bd1680a4e20ae..9edf18b04e7e29b12fa3808aab2239efcc404dca
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "lockfile.h"
  #include "refs.h"
  #include "object.h"
  #include "tag.h"
@@@ -70,10 -69,17 +70,10 @@@ static int check_refname_component(cons
  out:
        if (cp == refname)
                return 0; /* Component has zero length. */
 -      if (refname[0] == '.') {
 -              if (!(flags & REFNAME_DOT_COMPONENT))
 -                      return -1; /* Component starts with '.'. */
 -              /*
 -               * Even if leading dots are allowed, don't allow "."
 -               * as a component (".." is prevented by a rule above).
 -               */
 -              if (refname[1] == '\0')
 -                      return -1; /* Component equals ".". */
 -      }
 -      if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
 +      if (refname[0] == '.')
 +              return -1; /* Component starts with '.'. */
 +      if (cp - refname >= LOCK_SUFFIX_LEN &&
 +          !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
                return -1; /* Refname ends with ".lock". */
        return cp - refname;
  }
@@@ -187,8 -193,8 +187,8 @@@ struct ref_dir 
  
  /*
   * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
 - * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
 - * refs.h.
 + * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
 + * public values; see refs.h.
   */
  
  /*
   * the correct peeled value for the reference, which might be
   * null_sha1 if the reference is not a tag or if it is broken.
   */
 -#define REF_KNOWS_PEELED 0x08
 +#define REF_KNOWS_PEELED 0x10
  
  /* ref_entry represents a directory of references */
 -#define REF_DIR 0x10
 +#define REF_DIR 0x20
  
  /*
   * Entry has not yet been read from disk (used only for REF_DIR
   * entries representing loose references)
   */
 -#define REF_INCOMPLETE 0x20
 +#define REF_INCOMPLETE 0x40
  
  /*
   * A ref_entry represents either a reference or a "subdirectory" of
@@@ -274,39 -280,6 +274,39 @@@ static struct ref_dir *get_ref_dir(stru
        return dir;
  }
  
 +/*
 + * Check if a refname is safe.
 + * For refs that start with "refs/" we consider it safe as long they do
 + * not try to resolve to outside of refs/.
 + *
 + * For all other refs we only consider them safe iff they only contain
 + * upper case characters and '_' (like "HEAD" AND "MERGE_HEAD", and not like
 + * "config").
 + */
 +static int refname_is_safe(const char *refname)
 +{
 +      if (starts_with(refname, "refs/")) {
 +              char *buf;
 +              int result;
 +
 +              buf = xmalloc(strlen(refname) + 1);
 +              /*
 +               * Does the refname try to escape refs/?
 +               * For example: refs/foo/../bar is safe but refs/foo/../../bar
 +               * is not.
 +               */
 +              result = !normalize_path_copy(buf, refname + strlen("refs/"));
 +              free(buf);
 +              return result;
 +      }
 +      while (*refname) {
 +              if (!isupper(*refname) && *refname != '_')
 +                      return 0;
 +              refname++;
 +      }
 +      return 1;
 +}
 +
  static struct ref_entry *create_ref_entry(const char *refname,
                                          const unsigned char *sha1, int flag,
                                          int check_name)
        struct ref_entry *ref;
  
        if (check_name &&
 -          check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
 +          check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                die("Reference has invalid format: '%s'", refname);
 +      if (!check_name && !refname_is_safe(refname))
 +              die("Reference has invalid name: '%s'", refname);
        len = strlen(refname) + 1;
        ref = xmalloc(sizeof(struct ref_entry) + len);
        hashcpy(ref->u.value.sha1, sha1);
@@@ -814,13 -785,13 +814,13 @@@ static void prime_ref_dir(struct ref_di
        }
  }
  
 -static int entry_matches(struct ref_entry *entry, const char *refname)
 +static int entry_matches(struct ref_entry *entry, const struct string_list *list)
  {
 -      return refname && !strcmp(entry->name, refname);
 +      return list && string_list_has_string(list, entry->name);
  }
  
  struct nonmatching_ref_data {
 -      const char *skip;
 +      const struct string_list *skip;
        struct ref_entry *found;
  };
  
@@@ -844,19 -815,16 +844,19 @@@ static void report_refname_conflict(str
  /*
   * Return true iff a reference named refname could be created without
   * conflicting with the name of an existing reference in dir.  If
 - * oldrefname is non-NULL, ignore potential conflicts with oldrefname
 - * (e.g., because oldrefname is scheduled for deletion in the same
 + * skip is non-NULL, ignore potential conflicts with refs in skip
 + * (e.g., because they are scheduled for deletion in the same
   * operation).
   *
   * Two reference names conflict if one of them exactly matches the
   * leading components of the other; e.g., "foo/bar" conflicts with
   * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
   * "foo/barbados".
 + *
 + * skip must be sorted.
   */
 -static int is_refname_available(const char *refname, const char *oldrefname,
 +static int is_refname_available(const char *refname,
 +                              const struct string_list *skip,
                                struct ref_dir *dir)
  {
        const char *slash;
                 * looking for a conflict with a leaf entry.
                 *
                 * If we find one, we still must make sure it is
 -               * not "oldrefname".
 +               * not in "skip".
                 */
                pos = search_ref_dir(dir, refname, slash - refname);
                if (pos >= 0) {
                        struct ref_entry *entry = dir->entries[pos];
 -                      if (entry_matches(entry, oldrefname))
 +                      if (entry_matches(entry, skip))
                                return 1;
                        report_refname_conflict(entry, refname);
                        return 0;
                /*
                 * We found a directory named "refname". It is a
                 * problem iff it contains any ref that is not
 -               * "oldrefname".
 +               * in "skip".
                 */
                struct ref_entry *entry = dir->entries[pos];
                struct ref_dir *dir = get_ref_dir(entry);
                struct nonmatching_ref_data data;
  
 -              data.skip = oldrefname;
 +              data.skip = skip;
                sort_ref_dir(dir);
                if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
                        return 1;
@@@ -1068,10 -1036,8 +1068,10 @@@ static const char PACKED_REFS_HEADER[] 
   * Return a pointer to the refname within the line (null-terminated),
   * or NULL if there was a problem.
   */
 -static const char *parse_ref_line(char *line, unsigned char *sha1)
 +static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
  {
 +      const char *ref;
 +
        /*
         * 42: the answer to everything.
         *
         *  +1 (space in between hex and name)
         *  +1 (newline at the end of the line)
         */
 -      int len = strlen(line) - 42;
 -
 -      if (len <= 0)
 +      if (line->len <= 42)
                return NULL;
 -      if (get_sha1_hex(line, sha1) < 0)
 +
 +      if (get_sha1_hex(line->buf, sha1) < 0)
                return NULL;
 -      if (!isspace(line[40]))
 +      if (!isspace(line->buf[40]))
                return NULL;
 -      line += 41;
 -      if (isspace(*line))
 +
 +      ref = line->buf + 41;
 +      if (isspace(*ref))
                return NULL;
 -      if (line[len] != '\n')
 +
 +      if (line->buf[line->len - 1] != '\n')
                return NULL;
 -      line[len] = 0;
 +      line->buf[--line->len] = 0;
  
 -      return line;
 +      return ref;
  }
  
  /*
  static void read_packed_refs(FILE *f, struct ref_dir *dir)
  {
        struct ref_entry *last = NULL;
 -      char refline[PATH_MAX];
 +      struct strbuf line = STRBUF_INIT;
        enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
  
 -      while (fgets(refline, sizeof(refline), f)) {
 +      while (strbuf_getwholeline(&line, f, '\n') != EOF) {
                unsigned char sha1[20];
                const char *refname;
 -              static const char header[] = "# pack-refs with:";
 +              const char *traits;
  
 -              if (!strncmp(refline, header, sizeof(header)-1)) {
 -                      const char *traits = refline + sizeof(header) - 1;
 +              if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
                        if (strstr(traits, " fully-peeled "))
                                peeled = PEELED_FULLY;
                        else if (strstr(traits, " peeled "))
                        continue;
                }
  
 -              refname = parse_ref_line(refline, sha1);
 +              refname = parse_ref_line(&line, sha1);
                if (refname) {
 -                      last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
 +                      int flag = REF_ISPACKED;
 +
 +                      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 +                              hashclr(sha1);
 +                              flag |= REF_BAD_NAME | REF_ISBROKEN;
 +                      }
 +                      last = create_ref_entry(refname, sha1, flag, 0);
                        if (peeled == PEELED_FULLY ||
                            (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
                        continue;
                }
                if (last &&
 -                  refline[0] == '^' &&
 -                  strlen(refline) == PEELED_LINE_LENGTH &&
 -                  refline[PEELED_LINE_LENGTH - 1] == '\n' &&
 -                  !get_sha1_hex(refline + 1, sha1)) {
 +                  line.buf[0] == '^' &&
 +                  line.len == PEELED_LINE_LENGTH &&
 +                  line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
 +                  !get_sha1_hex(line.buf + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
                        /*
                         * Regardless of what the file header said,
                        last->flag |= REF_KNOWS_PEELED;
                }
        }
 +
 +      strbuf_release(&line);
  }
  
  /*
@@@ -1288,19 -1246,12 +1288,19 @@@ static void read_loose_refs(const char 
                                        hashclr(sha1);
                                        flag |= REF_ISBROKEN;
                                }
 -                      } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
 +                      } else if (read_ref_full(refname.buf,
 +                                               RESOLVE_REF_READING,
 +                                               sha1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        }
 +                      if (check_refname_format(refname.buf,
 +                                               REFNAME_ALLOW_ONELEVEL)) {
 +                              hashclr(sha1);
 +                              flag |= REF_BAD_NAME | REF_ISBROKEN;
 +                      }
                        add_entry_to_dir(dir,
 -                                       create_ref_entry(refname.buf, sha1, flag, 1));
 +                                       create_ref_entry(refname.buf, sha1, flag, 0));
                }
                strbuf_setlen(&refname, dirnamelen);
        }
@@@ -1419,10 -1370,10 +1419,10 @@@ static struct ref_entry *get_packed_ref
   * A loose ref file doesn't exist; check for a packed ref.  The
   * options are forwarded from resolve_safe_unsafe().
   */
 -static const char *handle_missing_loose_ref(const char *refname,
 -                                          unsigned char *sha1,
 -                                          int reading,
 -                                          int *flag)
 +static int resolve_missing_loose_ref(const char *refname,
 +                                   int resolve_flags,
 +                                   unsigned char *sha1,
 +                                   int *flags)
  {
        struct ref_entry *entry;
  
        entry = get_packed_ref(refname);
        if (entry) {
                hashcpy(sha1, entry->u.value.sha1);
 -              if (flag)
 -                      *flag |= REF_ISPACKED;
 -              return refname;
 +              if (flags)
 +                      *flags |= REF_ISPACKED;
 +              return 0;
        }
        /* The reference is not a packed reference, either. */
 -      if (reading) {
 -              return NULL;
 +      if (resolve_flags & RESOLVE_REF_READING) {
 +              errno = ENOENT;
 +              return -1;
        } else {
                hashclr(sha1);
 -              return refname;
 +              return 0;
        }
  }
  
  /* 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)
 +const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
  {
        int depth = MAXDEPTH;
        ssize_t len;
        char buffer[256];
        static char refname_buffer[256];
 +      int bad_name = 0;
  
 -      if (flag)
 -              *flag = 0;
 +      if (flags)
 +              *flags = 0;
  
        if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 -              errno = EINVAL;
 -              return NULL;
 -      }
 +              if (flags)
 +                      *flags |= REF_BAD_NAME;
  
 +              if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
 +                  !refname_is_safe(refname)) {
 +                      errno = EINVAL;
 +                      return NULL;
 +              }
 +              /*
 +               * dwim_ref() uses REF_ISBROKEN to distinguish between
 +               * missing refs and refs that were present but invalid,
 +               * to complain about the latter to stderr.
 +               *
 +               * We don't know whether the ref exists, so don't set
 +               * REF_ISBROKEN yet.
 +               */
 +              bad_name = 1;
 +      }
        for (;;) {
                char path[PATH_MAX];
                struct stat st;
                 */
        stat_ref:
                if (lstat(path, &st) < 0) {
 -                      if (errno == ENOENT)
 -                              return handle_missing_loose_ref(refname, sha1,
 -                                                              reading, flag);
 -                      else
 +                      if (errno != ENOENT)
 +                              return NULL;
 +                      if (resolve_missing_loose_ref(refname, resolve_flags,
 +                                                    sha1, flags))
                                return NULL;
 +                      if (bad_name) {
 +                              hashclr(sha1);
 +                              if (flags)
 +                                      *flags |= REF_ISBROKEN;
 +                      }
 +                      return refname;
                }
  
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                                        !check_refname_format(buffer, 0)) {
                                strcpy(refname_buffer, buffer);
                                refname = refname_buffer;
 -                              if (flag)
 -                                      *flag |= REF_ISSYMREF;
 +                              if (flags)
 +                                      *flags |= REF_ISSYMREF;
 +                              if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
 +                                      hashclr(sha1);
 +                                      return refname;
 +                              }
                                continue;
                        }
                }
                         */
                        if (get_sha1_hex(buffer, sha1) ||
                            (buffer[40] != '\0' && !isspace(buffer[40]))) {
 -                              if (flag)
 -                                      *flag |= REF_ISBROKEN;
 +                              if (flags)
 +                                      *flags |= REF_ISBROKEN;
                                errno = EINVAL;
                                return NULL;
                        }
 +                      if (bad_name) {
 +                              hashclr(sha1);
 +                              if (flags)
 +                                      *flags |= REF_ISBROKEN;
 +                      }
                        return refname;
                }
 -              if (flag)
 -                      *flag |= REF_ISSYMREF;
 +              if (flags)
 +                      *flags |= REF_ISSYMREF;
                buf = buffer + 4;
                while (isspace(*buf))
                        buf++;
 +              refname = strcpy(refname_buffer, buf);
 +              if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
 +                      hashclr(sha1);
 +                      return refname;
 +              }
                if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
 -                      if (flag)
 -                              *flag |= REF_ISBROKEN;
 -                      errno = EINVAL;
 -                      return NULL;
 +                      if (flags)
 +                              *flags |= REF_ISBROKEN;
 +
 +                      if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
 +                          !refname_is_safe(buf)) {
 +                              errno = EINVAL;
 +                              return NULL;
 +                      }
 +                      bad_name = 1;
                }
 -              refname = strcpy(refname_buffer, buf);
        }
  }
  
 -char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
 +char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
  {
-       const char *ret = resolve_ref_unsafe(ref, resolve_flags, sha1, flags);
-       return ret ? xstrdup(ret) : NULL;
 -      return xstrdup_or_null(resolve_ref_unsafe(ref, sha1, reading, flag));
++      return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
  }
  
  /* The argument to filter_refs */
@@@ -1629,22 -1539,22 +1628,22 @@@ struct ref_filter 
        void *cb_data;
  };
  
 -int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
 +int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
  {
 -      if (resolve_ref_unsafe(refname, sha1, reading, flags))
 +      if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
                return 0;
        return -1;
  }
  
  int read_ref(const char *refname, unsigned char *sha1)
  {
 -      return read_ref_full(refname, sha1, 1, NULL);
 +      return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
  }
  
  int ref_exists(const char *refname)
  {
        unsigned char sha1[20];
 -      return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
 +      return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
  }
  
  static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
@@@ -1757,7 -1667,7 +1756,7 @@@ int peel_ref(const char *refname, unsig
                return 0;
        }
  
 -      if (read_ref_full(refname, base, 1, &flag))
 +      if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
                return -1;
  
        /*
@@@ -1798,7 -1708,7 +1797,7 @@@ static int warn_if_dangling_symref(cons
        if (!(flags & REF_ISSYMREF))
                return 0;
  
 -      resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
 +      resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
        if (!resolves_to
            || (d->refname
                ? strcmp(resolves_to, d->refname)
@@@ -1923,7 -1833,7 +1922,7 @@@ static int do_head_ref(const char *subm
                return 0;
        }
  
 -      if (!read_ref_full("HEAD", sha1, 1, &flag))
 +      if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
                return fn("HEAD", sha1, flag, cb_data);
  
        return 0;
@@@ -2003,7 -1913,7 +2002,7 @@@ int head_ref_namespaced(each_ref_fn fn
        int flag;
  
        strbuf_addf(&buf, "%sHEAD", get_git_namespace());
 -      if (!read_ref_full(buf.buf, sha1, 1, &flag))
 +      if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
                ret = fn(buf.buf, sha1, flag, cb_data);
        strbuf_release(&buf);
  
@@@ -2098,9 -2008,7 +2097,9 @@@ int refname_match(const char *abbrev_na
  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)) {
 +      if (read_ref_full(lock->ref_name,
 +                        mustexist ? RESOLVE_REF_READING : 0,
 +                        lock->old_sha1, NULL)) {
                int save_errno = errno;
                error("Can't verify ref %s", lock->ref_name);
                unlock_ref(lock);
@@@ -2173,8 -2081,7 +2172,8 @@@ int dwim_ref(const char *str, int len, 
  
                this_result = refs_found ? sha1_from_ref : sha1;
                mksnpath(fullref, sizeof(fullref), *p, len, str);
 -              r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
 +              r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
 +                                     this_result, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
@@@ -2203,8 -2110,7 +2202,8 @@@ int dwim_log(const char *str, int len, 
                const char *ref, *it;
  
                mksnpath(path, sizeof(path), *p, len, str);
 -              ref = resolve_ref_unsafe(path, hash, 1, NULL);
 +              ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
 +                                       hash, NULL);
                if (!ref)
                        continue;
                if (reflog_exists(path))
  }
  
  /*
 - * Locks a "refs/" ref returning the lock on success and NULL on failure.
 + * Locks a ref returning the lock on success and NULL on failure.
   * On failure errno is set to something meaningful.
   */
  static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
 +                                          const struct string_list *skip,
                                            int flags, int *type_p)
  {
        char *ref_file;
        int last_errno = 0;
        int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 +      int resolve_flags = 0;
        int missing = 0;
        int attempts_remaining = 3;
  
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
  
 -      refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
 +      if (mustexist)
 +              resolve_flags |= RESOLVE_REF_READING;
 +      if (flags & REF_DELETING) {
 +              resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
 +              if (flags & REF_NODEREF)
 +                      resolve_flags |= RESOLVE_REF_NO_RECURSE;
 +      }
 +
 +      refname = resolve_ref_unsafe(refname, resolve_flags,
 +                                   lock->old_sha1, &type);
        if (!refname && errno == EISDIR) {
                /* we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                        error("there are still refs under '%s'", orig_refname);
                        goto error_return;
                }
 -              refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
 +              refname = resolve_ref_unsafe(orig_refname, resolve_flags,
 +                                           lock->old_sha1, &type);
        }
        if (type_p)
            *type_p = type;
         * name is a proper prefix of our refname.
         */
        if (missing &&
 -           !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
 +           !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
                last_errno = ENOTDIR;
                goto error_return;
        }
        lflags = 0;
        if (flags & REF_NODEREF) {
                refname = orig_refname;
 -              lflags |= LOCK_NODEREF;
 +              lflags |= LOCK_NO_DEREF;
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
  
        lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        if (lock->lock_fd < 0) {
 +              last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         * Maybe somebody just deleted one of the
                         * again:
                         */
                        goto retry;
 -              else
 -                      unable_to_lock_index_die(ref_file, errno);
 +              else {
 +                      struct strbuf err = STRBUF_INIT;
 +                      unable_to_lock_message(ref_file, errno, &err);
 +                      error("%s", err.buf);
 +                      strbuf_release(&err);
 +                      goto error_return;
 +              }
        }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
  
@@@ -2350,7 -2238,9 +2349,7 @@@ struct ref_lock *lock_any_ref_for_updat
                                         const unsigned char *old_sha1,
                                         int flags, int *type_p)
  {
 -      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 -              return NULL;
 -      return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
 +      return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
  }
  
  /*
@@@ -2416,13 -2306,16 +2415,13 @@@ int commit_packed_refs(void
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
  
 -      out = fdopen(packed_ref_cache->lock->fd, "w");
 +      out = fdopen_lock_file(packed_ref_cache->lock, "w");
        if (!out)
                die_errno("unable to fdopen packed-refs descriptor");
  
        fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
        do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
                                 0, write_packed_entry_fn, out);
 -      if (fclose(out))
 -              die_errno("write error");
 -      packed_ref_cache->lock->fd = -1;
  
        if (commit_lock_file(packed_ref_cache->lock)) {
                save_errno = errno;
@@@ -2552,8 -2445,8 +2551,8 @@@ static void prune_ref(struct ref_to_pru
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, r->name, r->sha1,
 -                                 REF_ISPRUNING, 1, &err) ||
 -          ref_transaction_commit(transaction, NULL, &err)) {
 +                                 REF_ISPRUNING, 1, NULL, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
                error("%s", err.buf);
                strbuf_release(&err);
@@@ -2617,7 -2510,7 +2616,7 @@@ static int curate_packed_ref_fn(struct 
                unsigned char sha1[20];
                int flags;
  
 -              if (read_ref_full(entry->name, sha1, 0, &flags))
 +              if (read_ref_full(entry->name, 0, sha1, &flags))
                        /* We should at least have found the packed ref. */
                        die("Internal error");
                if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
        return 0;
  }
  
 -int repack_without_refs(const char **refnames, int n, struct strbuf *err)
 +int repack_without_refs(struct string_list *refnames, 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, ret, removed = 0;
 +      struct string_list_item *refname, *ref_to_delete;
 +      int ret, needs_repacking = 0, removed = 0;
 +
 +      assert(err);
  
        /* Look for a packed ref */
 -      for (i = 0; i < n; i++)
 -              if (get_packed_ref(refnames[i]))
 +      for_each_string_list_item(refname, refnames) {
 +              if (get_packed_ref(refname->string)) {
 +                      needs_repacking = 1;
                        break;
 +              }
 +      }
  
        /* Avoid locking if we have nothing to do */
 -      if (i == n)
 +      if (!needs_repacking)
                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]);
 +              unable_to_lock_message(git_path("packed-refs"), errno, err);
 +              return -1;
        }
        packed = get_packed_refs(&ref_cache);
  
        /* Remove refnames from the cache */
 -      for (i = 0; i < n; i++)
 -              if (remove_entry(packed, refnames[i]) != -1)
 +      for_each_string_list_item(refname, refnames)
 +              if (remove_entry(packed, refname->string) != -1)
                        removed = 1;
        if (!removed) {
                /*
  
        /* Write what remains */
        ret = commit_packed_refs();
 -      if (ret && err)
 +      if (ret)
                strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
                            strerror(errno));
        return ret;
  }
  
 -static int delete_ref_loose(struct ref_lock *lock, int flag)
 +static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
  {
 -      if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
 -              /* loose */
 -              int err, i = strlen(lock->lk->filename) - 5; /* .lock */
 +      assert(err);
  
 -              lock->lk->filename[i] = 0;
 -              err = unlink_or_warn(lock->lk->filename);
 -              lock->lk->filename[i] = '.';
 -              if (err && errno != ENOENT)
 +      if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
 +              /*
 +               * loose.  The loose file name is the same as the
 +               * lockfile name, minus ".lock":
 +               */
 +              char *loose_filename = get_locked_file_path(lock->lk);
 +              int res = unlink_or_msg(loose_filename, err);
 +              free(loose_filename);
 +              if (res)
                        return 1;
        }
        return 0;
@@@ -2730,8 -2620,8 +2729,8 @@@ int delete_ref(const char *refname, con
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, sha1, delopt,
 -                                 sha1 && !is_null_sha1(sha1), &err) ||
 -          ref_transaction_commit(transaction, NULL, &err)) {
 +                                 sha1 && !is_null_sha1(sha1), NULL, &err) ||
 +          ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                ref_transaction_free(transaction);
                strbuf_release(&err);
@@@ -2796,21 -2686,6 +2795,21 @@@ static int rename_tmp_log(const char *n
        return 0;
  }
  
 +static int rename_ref_available(const char *oldname, const char *newname)
 +{
 +      struct string_list skip = STRING_LIST_INIT_NODUP;
 +      int ret;
 +
 +      string_list_insert(&skip, oldname);
 +      ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
 +          && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
 +      string_list_clear(&skip, 0);
 +      return ret;
 +}
 +
 +static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
 +                        const char *logmsg);
 +
  int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
        unsigned char sha1[20], orig_sha1[20];
        if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldrefname);
  
 -      symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
 +      symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
 +                                  orig_sha1, &flag);
        if (flag & REF_ISSYMREF)
                return error("refname %s is a symbolic ref, renaming it is not supported",
                        oldrefname);
        if (!symref)
                return error("refname %s not found", oldrefname);
  
 -      if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
 -              return 1;
 -
 -      if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
 +      if (!rename_ref_available(oldrefname, newrefname))
                return 1;
  
        if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
                goto rollback;
        }
  
 -      if (!read_ref_full(newrefname, sha1, 1, &flag) &&
 +      if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
            delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
                        if (remove_empty_directories(git_path("%s", newrefname))) {
  
        logmoved = log;
  
 -      lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
 +      lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for update", newrefname);
                goto rollback;
        return 0;
  
   rollback:
 -      lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
 +      lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
        if (!lock) {
                error("unable to lock %s for rollback", oldrefname);
                goto rollbacklog;
@@@ -2975,10 -2852,10 +2974,10 @@@ int log_ref_setup(const char *refname, 
  
        logfd = open(logfile, oflags, 0666);
        if (logfd < 0) {
 -              if (!(oflags & O_CREAT) && errno == ENOENT)
 +              if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
                        return 0;
  
 -              if ((oflags & O_CREAT) && errno == EISDIR) {
 +              if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
                                int save_errno = errno;
                                error("There are still logs under '%s'",
@@@ -3056,11 -2933,8 +3055,11 @@@ 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,
 +/*
 + * Write sha1 into the ref specified by the lock. Make sure that errno
 + * is sane on error.
 + */
 +static int write_ref_sha1(struct ref_lock *lock,
        const unsigned char *sha1, const char *logmsg)
  {
        static char term = '\n';
            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);
 +              error("Couldn't write %s", lock->lk->filename.buf);
                unlock_ref(lock);
                errno = save_errno;
                return -1;
                unsigned char head_sha1[20];
                int head_flag;
                const char *head_ref;
 -              head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
 +              head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
 +                                            head_sha1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
                    !strcmp(head_ref, lock->ref_name))
                        log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@@ -3417,54 -3290,29 +3416,54 @@@ int for_each_reflog_ent_reverse(const c
  
                        bp = find_beginning_of_line(buf, scanp);
  
 -                      if (*bp != '\n') {
 -                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
 -                              if (pos)
 -                                      break; /* need to fill another block */
 -                              scanp = buf - 1; /* leave loop */
 -                      } else {
 +                      if (*bp == '\n') {
                                /*
 -                               * (bp + 1) thru endp is the beginning of the
 -                               * current line we have in sb
 +                               * The newline is the end of the previous line,
 +                               * so we know we have complete line starting
 +                               * at (bp + 1). Prefix it onto any prior data
 +                               * we collected for the line and process it.
                                 */
                                strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
                                scanp = bp;
                                endp = bp + 1;
 +                              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +                              strbuf_reset(&sb);
 +                              if (ret)
 +                                      break;
 +                      } else if (!pos) {
 +                              /*
 +                               * We are at the start of the buffer, and the
 +                               * start of the file; there is no previous
 +                               * line, and we have everything for this one.
 +                               * Process it, and we can end the loop.
 +                               */
 +                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
 +                              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +                              strbuf_reset(&sb);
 +                              break;
                        }
 -                      ret = show_one_reflog_ent(&sb, fn, cb_data);
 -                      strbuf_reset(&sb);
 -                      if (ret)
 +
 +                      if (bp == buf) {
 +                              /*
 +                               * We are at the start of the buffer, and there
 +                               * is more file to read backwards. Which means
 +                               * we are in the middle of a line. Note that we
 +                               * may get here even if *bp was a newline; that
 +                               * just means we are at the exact end of the
 +                               * previous line, rather than some spot in the
 +                               * middle.
 +                               *
 +                               * Save away what we have to be combined with
 +                               * the data from the next read.
 +                               */
 +                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
                                break;
 +                      }
                }
  
        }
        if (!ret && sb.len)
 -              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +              die("BUG: reverse reflog parser had leftover data");
  
        fclose(logfp);
        strbuf_release(&sb);
@@@ -3518,7 -3366,7 +3517,7 @@@ static int do_for_each_reflog(struct st
                                retval = do_for_each_reflog(name, fn, cb_data);
                        } else {
                                unsigned char sha1[20];
 -                              if (read_ref_full(name->buf, sha1, 0, NULL))
 +                              if (read_ref_full(name->buf, 0, sha1, NULL))
                                        retval = error("bad ref for %s", name->buf);
                                else
                                        retval = fn(name->buf, sha1, 0, cb_data);
@@@ -3555,7 -3403,6 +3554,7 @@@ struct ref_update 
        int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
        struct ref_lock *lock;
        int type;
 +      char *msg;
        const char refname[FLEX_ARRAY];
  };
  
@@@ -3588,8 -3435,6 +3587,8 @@@ struct ref_transaction 
  
  struct ref_transaction *ref_transaction_begin(struct strbuf *err)
  {
 +      assert(err);
 +
        return xcalloc(1, sizeof(struct ref_transaction));
  }
  
@@@ -3600,10 -3445,9 +3599,10 @@@ void ref_transaction_free(struct ref_tr
        if (!transaction)
                return;
  
 -      for (i = 0; i < transaction->nr; i++)
 +      for (i = 0; i < transaction->nr; i++) {
 +              free(transaction->updates[i]->msg);
                free(transaction->updates[i]);
 -
 +      }
        free(transaction->updates);
        free(transaction);
  }
@@@ -3624,80 -3468,57 +3623,80 @@@ int ref_transaction_update(struct ref_t
                           const char *refname,
                           const unsigned char *new_sha1,
                           const unsigned char *old_sha1,
 -                         int flags, int have_old,
 +                         int flags, int have_old, const char *msg,
                           struct strbuf *err)
  {
        struct ref_update *update;
  
 +      assert(err);
 +
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: update called for transaction that is not open");
  
        if (have_old && !old_sha1)
                die("BUG: have_old is true but old_sha1 is NULL");
  
 +      if (!is_null_sha1(new_sha1) &&
 +          check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 +              strbuf_addf(err, "refusing to update ref with bad name %s",
 +                          refname);
 +              return -1;
 +      }
 +
        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);
 +      if (msg)
 +              update->msg = xstrdup(msg);
        return 0;
  }
  
  int ref_transaction_create(struct ref_transaction *transaction,
                           const char *refname,
                           const unsigned char *new_sha1,
 -                         int flags,
 +                         int flags, const char *msg,
                           struct strbuf *err)
  {
        struct ref_update *update;
  
 +      assert(err);
 +
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: create called for transaction that is not open");
  
        if (!new_sha1 || is_null_sha1(new_sha1))
                die("BUG: create ref with null new_sha1");
  
 +      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 +              strbuf_addf(err, "refusing to create ref with bad name %s",
 +                          refname);
 +              return -1;
 +      }
 +
        update = add_update(transaction, refname);
  
        hashcpy(update->new_sha1, new_sha1);
        hashclr(update->old_sha1);
        update->flags = flags;
        update->have_old = 1;
 +      if (msg)
 +              update->msg = xstrdup(msg);
        return 0;
  }
  
  int ref_transaction_delete(struct ref_transaction *transaction,
                           const char *refname,
                           const unsigned char *old_sha1,
 -                         int flags, int have_old,
 +                         int flags, int have_old, const char *msg,
                           struct strbuf *err)
  {
        struct ref_update *update;
  
 +      assert(err);
 +
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: delete called for transaction that is not open");
  
                assert(!is_null_sha1(old_sha1));
                hashcpy(update->old_sha1, old_sha1);
        }
 +      if (msg)
 +              update->msg = xstrdup(msg);
        return 0;
  }
  
@@@ -3726,8 -3545,8 +3725,8 @@@ int update_ref(const char *action, cons
        t = ref_transaction_begin(&err);
        if (!t ||
            ref_transaction_update(t, refname, sha1, oldval, flags,
 -                                 !!oldval, &err) ||
 -          ref_transaction_commit(t, action, &err)) {
 +                                 !!oldval, action, &err) ||
 +          ref_transaction_commit(t, &err)) {
                const char *str = "update_ref failed for ref '%s': %s";
  
                ref_transaction_free(t);
@@@ -3760,29 -3579,25 +3759,29 @@@ static int ref_update_reject_duplicates
                                        struct strbuf *err)
  {
        int i;
 +
 +      assert(err);
 +
        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.";
 -                      if (err)
 -                              strbuf_addf(err, str, updates[i]->refname);
 -
 +                      strbuf_addf(err,
 +                                  "Multiple updates for ref '%s' not allowed.",
 +                                  updates[i]->refname);
                        return 1;
                }
        return 0;
  }
  
  int ref_transaction_commit(struct ref_transaction *transaction,
 -                         const char *msg, struct strbuf *err)
 +                         struct strbuf *err)
  {
 -      int ret = 0, delnum = 0, i;
 -      const char **delnames;
 +      int ret = 0, i;
        int n = transaction->nr;
        struct ref_update **updates = transaction->updates;
 +      struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
 +      struct string_list_item *ref_to_delete;
 +
 +      assert(err);
  
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: commit called for transaction that is not open");
                return 0;
        }
  
 -      /* Allocate work space */
 -      delnames = xmalloc(sizeof(*delnames) * n);
 -
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
 -      ret = ref_update_reject_duplicates(updates, n, err);
 -      if (ret)
 +      if (ref_update_reject_duplicates(updates, n, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
 +      }
  
        /* Acquire all locks while verifying old values */
        for (i = 0; i < n; i++) {
                struct ref_update *update = updates[i];
 -
 -              update->lock = lock_any_ref_for_update(update->refname,
 -                                                     (update->have_old ?
 -                                                      update->old_sha1 :
 -                                                      NULL),
 -                                                     update->flags,
 -                                                     &update->type);
 +              int flags = update->flags;
 +
 +              if (is_null_sha1(update->new_sha1))
 +                      flags |= REF_DELETING;
 +              update->lock = lock_ref_sha1_basic(update->refname,
 +                                                 (update->have_old ?
 +                                                  update->old_sha1 :
 +                                                  NULL),
 +                                                 NULL,
 +                                                 flags,
 +                                                 &update->type);
                if (!update->lock) {
 -                      if (err)
 -                              strbuf_addf(err, "Cannot lock the ref '%s'.",
 -                                          update->refname);
 -                      ret = 1;
 +                      ret = (errno == ENOTDIR)
 +                              ? TRANSACTION_NAME_CONFLICT
 +                              : TRANSACTION_GENERIC_ERROR;
 +                      strbuf_addf(err, "Cannot lock the ref '%s'.",
 +                                  update->refname);
                        goto cleanup;
                }
        }
                struct ref_update *update = updates[i];
  
                if (!is_null_sha1(update->new_sha1)) {
 -                      ret = write_ref_sha1(update->lock, update->new_sha1,
 -                                           msg);
 -                      update->lock = NULL; /* freed by write_ref_sha1 */
 -                      if (ret) {
 -                              if (err)
 -                                      strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                                  update->refname);
 +                      if (write_ref_sha1(update->lock, update->new_sha1,
 +                                         update->msg)) {
 +                              update->lock = NULL; /* freed by write_ref_sha1 */
 +                              strbuf_addf(err, "Cannot update the ref '%s'.",
 +                                          update->refname);
 +                              ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
 +                      update->lock = NULL; /* freed by write_ref_sha1 */
                }
        }
  
                struct ref_update *update = updates[i];
  
                if (update->lock) {
 -                      ret |= delete_ref_loose(update->lock, update->type);
 +                      if (delete_ref_loose(update->lock, update->type, err)) {
 +                              ret = TRANSACTION_GENERIC_ERROR;
 +                              goto cleanup;
 +                      }
 +
                        if (!(update->flags & REF_ISPRUNING))
 -                              delnames[delnum++] = update->lock->ref_name;
 +                              string_list_append(&refs_to_delete,
 +                                                 update->lock->ref_name);
                }
        }
  
 -      ret |= repack_without_refs(delnames, delnum, err);
 -      for (i = 0; i < delnum; i++)
 -              unlink_or_warn(git_path("logs/%s", delnames[i]));
 +      if (repack_without_refs(&refs_to_delete, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +      for_each_string_list_item(ref_to_delete, &refs_to_delete)
 +              unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
        clear_loose_ref_cache(&ref_cache);
  
  cleanup:
        for (i = 0; i < n; i++)
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
 -      free(delnames);
 +      string_list_clear(&refs_to_delete, 0);
        return ret;
  }
  
diff --combined remote.c
index 5b9c6931c1e66adb03f5d505aa8dd57169452eae,179bceff8a2ce27f95de797656dd5460950c2d9c..7b71ebf4bfca0ce6490a122afc5c321d2b35690c
+++ b/remote.c
@@@ -508,7 -508,7 +508,7 @@@ static void read_config(void
                return;
        default_remote_name = "origin";
        current_branch = NULL;
 -      head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
 +      head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
            skip_prefix(head_ref, "refs/heads/", &head_ref)) {
                current_branch = make_branch(head_ref, 0);
@@@ -975,8 -975,8 +975,8 @@@ struct ref *copy_ref(const struct ref *
        cpy = xmalloc(sizeof(struct ref) + len + 1);
        memcpy(cpy, ref, sizeof(struct ref) + len + 1);
        cpy->next = NULL;
-       cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
-       cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+       cpy->symref = xstrdup_or_null(ref->symref);
+       cpy->remote_status = xstrdup_or_null(ref->remote_status);
        cpy->peer_ref = copy_ref(ref->peer_ref);
        return cpy;
  }
@@@ -1138,8 -1138,7 +1138,8 @@@ static char *guess_ref(const char *name
        struct strbuf buf = STRBUF_INIT;
        unsigned char sha1[20];
  
 -      const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
 +      const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
 +                                         sha1, NULL);
        if (!r)
                return NULL;
  
@@@ -1200,9 -1199,7 +1200,9 @@@ static int match_explicit(struct ref *s
                unsigned char sha1[20];
                int flag;
  
 -              dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
 +              dst_value = resolve_ref_unsafe(matched_src->name,
 +                                             RESOLVE_REF_READING,
 +                                             sha1, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
@@@ -1356,7 -1353,7 +1356,7 @@@ static void add_missing_tags(struct re
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
  
 -      sort_string_list(&dst_tag);
 +      string_list_sort(&dst_tag);
  
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
@@@ -1421,7 -1418,7 +1421,7 @@@ static void prepare_ref_index(struct st
        for ( ; ref; ref = ref->next)
                string_list_append_nodup(ref_index, ref->name)->util = ref;
  
 -      sort_string_list(ref_index);
 +      string_list_sort(ref_index);
  }
  
  /*
@@@ -1631,27 -1628,6 +1631,27 @@@ void set_ref_status_for_push(struct re
        }
  }
  
 +static void set_merge(struct branch *ret)
 +{
 +      char *ref;
 +      unsigned char sha1[20];
 +      int i;
 +
 +      ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
 +      for (i = 0; i < ret->merge_nr; i++) {
 +              ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
 +              ret->merge[i]->src = xstrdup(ret->merge_name[i]);
 +              if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
 +                  strcmp(ret->remote_name, "."))
 +                      continue;
 +              if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
 +                           sha1, &ref) == 1)
 +                      ret->merge[i]->dst = ref;
 +              else
 +                      ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
 +      }
 +}
 +
  struct branch *branch_get(const char *name)
  {
        struct branch *ret;
                ret = make_branch(name, 0);
        if (ret && ret->remote_name) {
                ret->remote = remote_get(ret->remote_name);
 -              if (ret->merge_nr) {
 -                      int i;
 -                      ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
 -                      for (i = 0; i < ret->merge_nr; i++) {
 -                              ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
 -                              ret->merge[i]->src = xstrdup(ret->merge_name[i]);
 -                              if (remote_find_tracking(ret->remote, ret->merge[i])
 -                                  && !strcmp(ret->remote_name, "."))
 -                                      ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
 -                      }
 -              }
 +              if (ret->merge_nr)
 +                      set_merge(ret);
        }
        return ret;
  }
@@@ -1688,7 -1673,7 +1688,7 @@@ static int ignore_symref_update(const c
        unsigned char sha1[20];
        int flag;
  
 -      if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
 +      if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
                return 0; /* non-existing refs are OK */
        return (flag & REF_ISSYMREF);
  }
@@@ -2147,7 -2132,7 +2147,7 @@@ struct ref *get_stale_heads(struct refs
        info.ref_count = ref_count;
        for (ref = fetch_map; ref; ref = ref->next)
                string_list_append(&ref_names, ref->name);
 -      sort_string_list(&ref_names);
 +      string_list_sort(&ref_names);
        for_each_ref(get_stale_heads_cb, &info);
        string_list_clear(&ref_names, 0);
        return stale_refs;
diff --combined shallow.c
index cdd07751461e69291588dee801c3563644cb1107,ee145743346f2c2fcafccfc04407c599dfe2eaae..f5e67204a4084ff79beec0d5a5dd0fb06966cc3f
+++ b/shallow.c
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "lockfile.h"
  #include "commit.h"
  #include "tag.h"
  #include "pkt-line.h"
@@@ -22,7 -21,7 +22,7 @@@ void set_alternate_shallow_file(const c
        if (alternate_shallow_file && !override)
                return;
        free(alternate_shallow_file);
-       alternate_shallow_file = path ? xstrdup(path) : NULL;
+       alternate_shallow_file = xstrdup_or_null(path);
  }
  
  int register_shallow(const unsigned char *sha1)
@@@ -227,6 -226,7 +227,6 @@@ static void remove_temporary_shallow_on
  
  const char *setup_temporary_shallow(const struct sha1_array *extra)
  {
 -      static int installed_handler;
        struct strbuf sb = STRBUF_INIT;
        int fd;
  
                strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
                fd = xmkstemp(temporary_shallow.buf);
  
 -              if (!installed_handler) {
 -                      atexit(remove_temporary_shallow);
 -                      sigchain_push_common(remove_temporary_shallow_on_signal);
 -              }
 +              atexit(remove_temporary_shallow);
 +              sigchain_push_common(remove_temporary_shallow_on_signal);
  
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
@@@ -267,8 -269,8 +267,8 @@@ void setup_alternate_shallow(struct loc
        if (write_shallow_commits(&sb, 0, extra)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
 -                                shallow_lock->filename);
 -              *alternate_shallow_file = shallow_lock->filename;
 +                                shallow_lock->filename.buf);
 +              *alternate_shallow_file = shallow_lock->filename.buf;
        } else
                /*
                 * is_repository_shallow() sees empty string as "no
@@@ -314,7 -316,7 +314,7 @@@ void prune_shallow(int show_only
        if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
 -                                shallow_lock.filename);
 +                                shallow_lock.filename.buf);
                commit_lock_file(&shallow_lock);
        } else {
                unlink(git_path("shallow"));
diff --combined walker.c
index f149371e71ebdcdbd12336aace15b0fc20f74569,adabcc9dfe7c9614d10c560dec53c210254e14f9..483da4e0fb5771b8a64d62648624258dcf3fdcb0
+++ b/walker.c
@@@ -232,7 -232,7 +232,7 @@@ int walker_targets_stdin(char ***target
                        REALLOC_ARRAY(*write_ref, targets_alloc);
                }
                (*target)[targets] = xstrdup(tg_one);
-               (*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
+               (*write_ref)[targets] = xstrdup_or_null(rf_one);
                targets++;
        }
        strbuf_release(&buf);
@@@ -300,13 -300,14 +300,13 @@@ int walker_fetch(struct walker *walker
                strbuf_addf(&refname, "refs/%s", write_ref[i]);
                if (ref_transaction_update(transaction, refname.buf,
                                           &sha1[20 * i], NULL, 0, 0,
 +                                         msg ? msg : "fetch (unknown)",
                                           &err)) {
                        error("%s", err.buf);
                        goto done;
                }
        }
 -      if (ref_transaction_commit(transaction,
 -                                 msg ? msg : "fetch (unknown)",
 -                                 &err)) {
 +      if (ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                goto done;
        }