Merge branch 'mh/tempfile'
authorJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:09 +0000 (14:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:09 +0000 (14:57 -0700)
The "lockfile" API has been rebuilt on top of a new "tempfile" API.

* mh/tempfile:
credential-cache--daemon: use tempfile module
credential-cache--daemon: delete socket from main()
gc: use tempfile module to handle gc.pid file
lock_repo_for_gc(): compute the path to "gc.pid" only once
diff: use tempfile module
setup_temporary_shallow(): use tempfile module
write_shared_index(): use tempfile module
register_tempfile(): new function to handle an existing temporary file
tempfile: add several functions for creating temporary files
prepare_tempfile_object(): new function, extracted from create_tempfile()
tempfile: a new module for handling temporary files
commit_lock_file(): use get_locked_file_path()
lockfile: add accessor get_lock_file_path()
lockfile: add accessors get_lock_file_fd() and get_lock_file_fp()
create_bundle(): duplicate file descriptor to avoid closing it twice
lockfile: move documentation to lockfile.h and lockfile.c

1  2 
Makefile
builtin/am.c
builtin/commit.c
builtin/gc.c
builtin/pull.c
config.c
diff.c
lockfile.c
read-cache.c
refs.c
shallow.c
diff --combined Makefile
index e39ca6ca6480618c032128df8d12aed1da81e78e,2573f8966952ca48e87db269b80473315264c24a..704f7e7c6ff55baa0172103506450489cf12cfe6
+++ b/Makefile
@@@ -217,11 -217,10 +217,11 @@@ all:
  # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
  #
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
 -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
 -# randomly break unless your underlying filesystem supports those sub-second
 -# times (my ext3 doesn't).
 +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
 +# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
 +# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
 +# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
 +# Documentation/technical/racy-git.txt for details.
  #
  # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
  # "st_ctim"
@@@ -467,6 -466,7 +467,6 @@@ TEST_PROGRAMS_NEED_X 
  # interactive shell sessions without exporting it.
  unexport CDPATH
  
 -SCRIPT_SH += git-am.sh
  SCRIPT_SH += git-bisect.sh
  SCRIPT_SH += git-difftool--helper.sh
  SCRIPT_SH += git-filter-branch.sh
@@@ -474,6 -474,7 +474,6 @@@ SCRIPT_SH += git-merge-octopus.s
  SCRIPT_SH += git-merge-one-file.sh
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
 -SCRIPT_SH += git-pull.sh
  SCRIPT_SH += git-quiltimport.sh
  SCRIPT_SH += git-rebase.sh
  SCRIPT_SH += git-remote-testgit.sh
@@@ -761,7 -762,6 +761,7 @@@ LIB_OBJS += reachable.
  LIB_OBJS += read-cache.o
  LIB_OBJS += reflog-walk.o
  LIB_OBJS += refs.o
 +LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
  LIB_OBJS += replace_object.o
  LIB_OBJS += rerere.o
@@@ -786,6 -786,7 +786,7 @@@ LIB_OBJS += string-list.
  LIB_OBJS += submodule.o
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
+ LIB_OBJS += tempfile.o
  LIB_OBJS += trace.o
  LIB_OBJS += trailer.o
  LIB_OBJS += transport.o
@@@ -812,7 -813,6 +813,7 @@@ LIB_OBJS += xdiff-interface.
  LIB_OBJS += zlib.o
  
  BUILTIN_OBJS += builtin/add.o
 +BUILTIN_OBJS += builtin/am.o
  BUILTIN_OBJS += builtin/annotate.o
  BUILTIN_OBJS += builtin/apply.o
  BUILTIN_OBJS += builtin/archive.o
@@@ -878,7 -878,6 +879,7 @@@ BUILTIN_OBJS += builtin/pack-refs.
  BUILTIN_OBJS += builtin/patch-id.o
  BUILTIN_OBJS += builtin/prune-packed.o
  BUILTIN_OBJS += builtin/prune.o
 +BUILTIN_OBJS += builtin/pull.o
  BUILTIN_OBJS += builtin/push.o
  BUILTIN_OBJS += builtin/read-tree.o
  BUILTIN_OBJS += builtin/receive-pack.o
@@@ -911,7 -910,6 +912,7 @@@ BUILTIN_OBJS += builtin/var.
  BUILTIN_OBJS += builtin/verify-commit.o
  BUILTIN_OBJS += builtin/verify-pack.o
  BUILTIN_OBJS += builtin/verify-tag.o
 +BUILTIN_OBJS += builtin/worktree.o
  BUILTIN_OBJS += builtin/write-tree.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
@@@ -1750,7 -1748,7 +1751,7 @@@ $(SCRIPT_PERL_GEN): perl/perl.ma
  perl/perl.mak: perl/PM.stamp
  
  perl/PM.stamp: FORCE
 -      $(QUIET_GEN)$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
 +      @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
        { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
        $(RM) $@+
  
@@@ -1787,7 -1785,7 +1788,7 @@@ GIT-PERL-DEFINES: FORC
  gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
 -git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
 +git-instaweb: git-instaweb.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -2106,47 -2104,46 +2107,47 @@@ GIT-LDFLAGS: FORC
  # that runs GIT-BUILD-OPTIONS, and then again to protect it
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: FORCE
 -      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
 -      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
 -      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
 -      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
 -      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
 -      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
 -      @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@
 -      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
 -      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 -      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 -      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
 +      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
 +      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
 +      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
 +      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
 +      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
 +      @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
 +      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
 +      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
 +      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
 +      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
  ifdef TEST_OUTPUT_DIRECTORY
 -      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
 +      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
  endif
  ifdef GIT_TEST_OPTS
 -      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 +      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP
 -      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
 +      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
 -      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
 +      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@+
  endif
 -      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
 -      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
 +      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+
 +      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@+
  ifdef GIT_PERF_REPEAT_COUNT
 -      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
 +      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@+
  endif
  ifdef GIT_PERF_REPO
 -      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
 +      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_LARGE_REPO
 -      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
 +      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_MAKE_OPTS
 -      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
 +      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
  endif
  ifdef TEST_GIT_INDEX_VERSION
 -      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@
 +      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
  endif
 +      @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
  
  ### Detect Python interpreter path changes
  ifndef NO_PYTHON
diff --combined builtin/am.c
index 634f7a7aa717e6da7ff9a549cb7d5c5585e10873,0000000000000000000000000000000000000000..b9c62e3de37bbb76e80a21a6e5e5760e1ba04360
mode 100644,000000..100644
--- /dev/null
@@@ -1,2351 -1,0 +1,2352 @@@
 +/*
 + * Builtin "git am"
 + *
 + * Based on git-am.sh by Junio C Hamano.
 + */
 +#include "cache.h"
 +#include "builtin.h"
 +#include "exec_cmd.h"
 +#include "parse-options.h"
 +#include "dir.h"
 +#include "run-command.h"
 +#include "quote.h"
++#include "tempfile.h"
 +#include "lockfile.h"
 +#include "cache-tree.h"
 +#include "refs.h"
 +#include "commit.h"
 +#include "diff.h"
 +#include "diffcore.h"
 +#include "unpack-trees.h"
 +#include "branch.h"
 +#include "sequencer.h"
 +#include "revision.h"
 +#include "merge-recursive.h"
 +#include "revision.h"
 +#include "log-tree.h"
 +#include "notes-utils.h"
 +#include "rerere.h"
 +#include "prompt.h"
 +
 +/**
 + * Returns 1 if the file is empty or does not exist, 0 otherwise.
 + */
 +static int is_empty_file(const char *filename)
 +{
 +      struct stat st;
 +
 +      if (stat(filename, &st) < 0) {
 +              if (errno == ENOENT)
 +                      return 1;
 +              die_errno(_("could not stat %s"), filename);
 +      }
 +
 +      return !st.st_size;
 +}
 +
 +/**
 + * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators.
 + */
 +static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
 +{
 +      if (strbuf_getwholeline(sb, fp, '\n'))
 +              return EOF;
 +      if (sb->buf[sb->len - 1] == '\n') {
 +              strbuf_setlen(sb, sb->len - 1);
 +              if (sb->len > 0 && sb->buf[sb->len - 1] == '\r')
 +                      strbuf_setlen(sb, sb->len - 1);
 +      }
 +      return 0;
 +}
 +
 +/**
 + * Returns the length of the first line of msg.
 + */
 +static int linelen(const char *msg)
 +{
 +      return strchrnul(msg, '\n') - msg;
 +}
 +
 +/**
 + * Returns true if `str` consists of only whitespace, false otherwise.
 + */
 +static int str_isspace(const char *str)
 +{
 +      for (; *str; str++)
 +              if (!isspace(*str))
 +                      return 0;
 +
 +      return 1;
 +}
 +
 +enum patch_format {
 +      PATCH_FORMAT_UNKNOWN = 0,
 +      PATCH_FORMAT_MBOX,
 +      PATCH_FORMAT_STGIT,
 +      PATCH_FORMAT_STGIT_SERIES,
 +      PATCH_FORMAT_HG
 +};
 +
 +enum keep_type {
 +      KEEP_FALSE = 0,
 +      KEEP_TRUE,      /* pass -k flag to git-mailinfo */
 +      KEEP_NON_PATCH  /* pass -b flag to git-mailinfo */
 +};
 +
 +enum scissors_type {
 +      SCISSORS_UNSET = -1,
 +      SCISSORS_FALSE = 0,  /* pass --no-scissors to git-mailinfo */
 +      SCISSORS_TRUE        /* pass --scissors to git-mailinfo */
 +};
 +
 +enum signoff_type {
 +      SIGNOFF_FALSE = 0,
 +      SIGNOFF_TRUE = 1,
 +      SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
 +};
 +
 +struct am_state {
 +      /* state directory path */
 +      char *dir;
 +
 +      /* current and last patch numbers, 1-indexed */
 +      int cur;
 +      int last;
 +
 +      /* commit metadata and message */
 +      char *author_name;
 +      char *author_email;
 +      char *author_date;
 +      char *msg;
 +      size_t msg_len;
 +
 +      /* when --rebasing, records the original commit the patch came from */
 +      unsigned char orig_commit[GIT_SHA1_RAWSZ];
 +
 +      /* number of digits in patch filename */
 +      int prec;
 +
 +      /* various operating modes and command line options */
 +      int interactive;
 +      int threeway;
 +      int quiet;
 +      int signoff; /* enum signoff_type */
 +      int utf8;
 +      int keep; /* enum keep_type */
 +      int message_id;
 +      int scissors; /* enum scissors_type */
 +      struct argv_array git_apply_opts;
 +      const char *resolvemsg;
 +      int committer_date_is_author_date;
 +      int ignore_date;
 +      int allow_rerere_autoupdate;
 +      const char *sign_commit;
 +      int rebasing;
 +};
 +
 +/**
 + * Initializes am_state with the default values. The state directory is set to
 + * dir.
 + */
 +static void am_state_init(struct am_state *state, const char *dir)
 +{
 +      int gpgsign;
 +
 +      memset(state, 0, sizeof(*state));
 +
 +      assert(dir);
 +      state->dir = xstrdup(dir);
 +
 +      state->prec = 4;
 +
 +      git_config_get_bool("am.threeway", &state->threeway);
 +
 +      state->utf8 = 1;
 +
 +      git_config_get_bool("am.messageid", &state->message_id);
 +
 +      state->scissors = SCISSORS_UNSET;
 +
 +      argv_array_init(&state->git_apply_opts);
 +
 +      if (!git_config_get_bool("commit.gpgsign", &gpgsign))
 +              state->sign_commit = gpgsign ? "" : NULL;
 +}
 +
 +/**
 + * Releases memory allocated by an am_state.
 + */
 +static void am_state_release(struct am_state *state)
 +{
 +      free(state->dir);
 +      free(state->author_name);
 +      free(state->author_email);
 +      free(state->author_date);
 +      free(state->msg);
 +      argv_array_clear(&state->git_apply_opts);
 +}
 +
 +/**
 + * Returns path relative to the am_state directory.
 + */
 +static inline const char *am_path(const struct am_state *state, const char *path)
 +{
 +      return mkpath("%s/%s", state->dir, path);
 +}
 +
 +/**
 + * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
 + * at the end.
 + */
 +static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
 +{
 +      va_list ap;
 +
 +      va_start(ap, fmt);
 +      if (!state->quiet) {
 +              vfprintf(fp, fmt, ap);
 +              putc('\n', fp);
 +      }
 +      va_end(ap);
 +}
 +
 +/**
 + * Returns 1 if there is an am session in progress, 0 otherwise.
 + */
 +static int am_in_progress(const struct am_state *state)
 +{
 +      struct stat st;
 +
 +      if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode))
 +              return 0;
 +      if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode))
 +              return 0;
 +      if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode))
 +              return 0;
 +      return 1;
 +}
 +
 +/**
 + * Reads the contents of `file` in the `state` directory into `sb`. Returns the
 + * number of bytes read on success, -1 if the file does not exist. If `trim` is
 + * set, trailing whitespace will be removed.
 + */
 +static int read_state_file(struct strbuf *sb, const struct am_state *state,
 +                      const char *file, int trim)
 +{
 +      strbuf_reset(sb);
 +
 +      if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) {
 +              if (trim)
 +                      strbuf_trim(sb);
 +
 +              return sb->len;
 +      }
 +
 +      if (errno == ENOENT)
 +              return -1;
 +
 +      die_errno(_("could not read '%s'"), am_path(state, file));
 +}
 +
 +/**
 + * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE
 + * as a newly-allocated string. VALUE must be a quoted string, and the KEY must
 + * match `key`. Returns NULL on failure.
 + *
 + * This is used by read_author_script() to read the GIT_AUTHOR_* variables from
 + * the author-script.
 + */
 +static char *read_shell_var(FILE *fp, const char *key)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      const char *str;
 +
 +      if (strbuf_getline(&sb, fp, '\n'))
 +              goto fail;
 +
 +      if (!skip_prefix(sb.buf, key, &str))
 +              goto fail;
 +
 +      if (!skip_prefix(str, "=", &str))
 +              goto fail;
 +
 +      strbuf_remove(&sb, 0, str - sb.buf);
 +
 +      str = sq_dequote(sb.buf);
 +      if (!str)
 +              goto fail;
 +
 +      return strbuf_detach(&sb, NULL);
 +
 +fail:
 +      strbuf_release(&sb);
 +      return NULL;
 +}
 +
 +/**
 + * Reads and parses the state directory's "author-script" file, and sets
 + * state->author_name, state->author_email and state->author_date accordingly.
 + * Returns 0 on success, -1 if the file could not be parsed.
 + *
 + * The author script is of the format:
 + *
 + *    GIT_AUTHOR_NAME='$author_name'
 + *    GIT_AUTHOR_EMAIL='$author_email'
 + *    GIT_AUTHOR_DATE='$author_date'
 + *
 + * where $author_name, $author_email and $author_date are quoted. We are strict
 + * with our parsing, as the file was meant to be eval'd in the old git-am.sh
 + * script, and thus if the file differs from what this function expects, it is
 + * better to bail out than to do something that the user does not expect.
 + */
 +static int read_author_script(struct am_state *state)
 +{
 +      const char *filename = am_path(state, "author-script");
 +      FILE *fp;
 +
 +      assert(!state->author_name);
 +      assert(!state->author_email);
 +      assert(!state->author_date);
 +
 +      fp = fopen(filename, "r");
 +      if (!fp) {
 +              if (errno == ENOENT)
 +                      return 0;
 +              die_errno(_("could not open '%s' for reading"), filename);
 +      }
 +
 +      state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME");
 +      if (!state->author_name) {
 +              fclose(fp);
 +              return -1;
 +      }
 +
 +      state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL");
 +      if (!state->author_email) {
 +              fclose(fp);
 +              return -1;
 +      }
 +
 +      state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE");
 +      if (!state->author_date) {
 +              fclose(fp);
 +              return -1;
 +      }
 +
 +      if (fgetc(fp) != EOF) {
 +              fclose(fp);
 +              return -1;
 +      }
 +
 +      fclose(fp);
 +      return 0;
 +}
 +
 +/**
 + * Saves state->author_name, state->author_email and state->author_date in the
 + * state directory's "author-script" file.
 + */
 +static void write_author_script(const struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_addstr(&sb, "GIT_AUTHOR_NAME=");
 +      sq_quote_buf(&sb, state->author_name);
 +      strbuf_addch(&sb, '\n');
 +
 +      strbuf_addstr(&sb, "GIT_AUTHOR_EMAIL=");
 +      sq_quote_buf(&sb, state->author_email);
 +      strbuf_addch(&sb, '\n');
 +
 +      strbuf_addstr(&sb, "GIT_AUTHOR_DATE=");
 +      sq_quote_buf(&sb, state->author_date);
 +      strbuf_addch(&sb, '\n');
 +
 +      write_file(am_path(state, "author-script"), 1, "%s", sb.buf);
 +
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Reads the commit message from the state directory's "final-commit" file,
 + * setting state->msg to its contents and state->msg_len to the length of its
 + * contents in bytes.
 + *
 + * Returns 0 on success, -1 if the file does not exist.
 + */
 +static int read_commit_msg(struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      assert(!state->msg);
 +
 +      if (read_state_file(&sb, state, "final-commit", 0) < 0) {
 +              strbuf_release(&sb);
 +              return -1;
 +      }
 +
 +      state->msg = strbuf_detach(&sb, &state->msg_len);
 +      return 0;
 +}
 +
 +/**
 + * Saves state->msg in the state directory's "final-commit" file.
 + */
 +static void write_commit_msg(const struct am_state *state)
 +{
 +      int fd;
 +      const char *filename = am_path(state, "final-commit");
 +
 +      fd = xopen(filename, O_WRONLY | O_CREAT, 0666);
 +      if (write_in_full(fd, state->msg, state->msg_len) < 0)
 +              die_errno(_("could not write to %s"), filename);
 +      close(fd);
 +}
 +
 +/**
 + * Loads state from disk.
 + */
 +static void am_load(struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      if (read_state_file(&sb, state, "next", 1) < 0)
 +              die("BUG: state file 'next' does not exist");
 +      state->cur = strtol(sb.buf, NULL, 10);
 +
 +      if (read_state_file(&sb, state, "last", 1) < 0)
 +              die("BUG: state file 'last' does not exist");
 +      state->last = strtol(sb.buf, NULL, 10);
 +
 +      if (read_author_script(state) < 0)
 +              die(_("could not parse author script"));
 +
 +      read_commit_msg(state);
 +
 +      if (read_state_file(&sb, state, "original-commit", 1) < 0)
 +              hashclr(state->orig_commit);
 +      else if (get_sha1_hex(sb.buf, state->orig_commit) < 0)
 +              die(_("could not parse %s"), am_path(state, "original-commit"));
 +
 +      read_state_file(&sb, state, "threeway", 1);
 +      state->threeway = !strcmp(sb.buf, "t");
 +
 +      read_state_file(&sb, state, "quiet", 1);
 +      state->quiet = !strcmp(sb.buf, "t");
 +
 +      read_state_file(&sb, state, "sign", 1);
 +      state->signoff = !strcmp(sb.buf, "t");
 +
 +      read_state_file(&sb, state, "utf8", 1);
 +      state->utf8 = !strcmp(sb.buf, "t");
 +
 +      read_state_file(&sb, state, "keep", 1);
 +      if (!strcmp(sb.buf, "t"))
 +              state->keep = KEEP_TRUE;
 +      else if (!strcmp(sb.buf, "b"))
 +              state->keep = KEEP_NON_PATCH;
 +      else
 +              state->keep = KEEP_FALSE;
 +
 +      read_state_file(&sb, state, "messageid", 1);
 +      state->message_id = !strcmp(sb.buf, "t");
 +
 +      read_state_file(&sb, state, "scissors", 1);
 +      if (!strcmp(sb.buf, "t"))
 +              state->scissors = SCISSORS_TRUE;
 +      else if (!strcmp(sb.buf, "f"))
 +              state->scissors = SCISSORS_FALSE;
 +      else
 +              state->scissors = SCISSORS_UNSET;
 +
 +      read_state_file(&sb, state, "apply-opt", 1);
 +      argv_array_clear(&state->git_apply_opts);
 +      if (sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) < 0)
 +              die(_("could not parse %s"), am_path(state, "apply-opt"));
 +
 +      state->rebasing = !!file_exists(am_path(state, "rebasing"));
 +
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Removes the am_state directory, forcefully terminating the current am
 + * session.
 + */
 +static void am_destroy(const struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_addstr(&sb, state->dir);
 +      remove_dir_recursively(&sb, 0);
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Runs applypatch-msg hook. Returns its exit code.
 + */
 +static int run_applypatch_msg_hook(struct am_state *state)
 +{
 +      int ret;
 +
 +      assert(state->msg);
 +      ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
 +
 +      if (!ret) {
 +              free(state->msg);
 +              state->msg = NULL;
 +              if (read_commit_msg(state) < 0)
 +                      die(_("'%s' was deleted by the applypatch-msg hook"),
 +                              am_path(state, "final-commit"));
 +      }
 +
 +      return ret;
 +}
 +
 +/**
 + * Runs post-rewrite hook. Returns it exit code.
 + */
 +static int run_post_rewrite_hook(const struct am_state *state)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      const char *hook = find_hook("post-rewrite");
 +      int ret;
 +
 +      if (!hook)
 +              return 0;
 +
 +      argv_array_push(&cp.args, hook);
 +      argv_array_push(&cp.args, "rebase");
 +
 +      cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
 +      cp.stdout_to_stderr = 1;
 +
 +      ret = run_command(&cp);
 +
 +      close(cp.in);
 +      return ret;
 +}
 +
 +/**
 + * Reads the state directory's "rewritten" file, and copies notes from the old
 + * commits listed in the file to their rewritten commits.
 + *
 + * Returns 0 on success, -1 on failure.
 + */
 +static int copy_notes_for_rebase(const struct am_state *state)
 +{
 +      struct notes_rewrite_cfg *c;
 +      struct strbuf sb = STRBUF_INIT;
 +      const char *invalid_line = _("Malformed input line: '%s'.");
 +      const char *msg = "Notes added by 'git rebase'";
 +      FILE *fp;
 +      int ret = 0;
 +
 +      assert(state->rebasing);
 +
 +      c = init_copy_notes_for_rewrite("rebase");
 +      if (!c)
 +              return 0;
 +
 +      fp = xfopen(am_path(state, "rewritten"), "r");
 +
 +      while (!strbuf_getline(&sb, fp, '\n')) {
 +              unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
 +
 +              if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
 +                      ret = error(invalid_line, sb.buf);
 +                      goto finish;
 +              }
 +
 +              if (get_sha1_hex(sb.buf, from_obj)) {
 +                      ret = error(invalid_line, sb.buf);
 +                      goto finish;
 +              }
 +
 +              if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
 +                      ret = error(invalid_line, sb.buf);
 +                      goto finish;
 +              }
 +
 +              if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
 +                      ret = error(invalid_line, sb.buf);
 +                      goto finish;
 +              }
 +
 +              if (copy_note_for_rewrite(c, from_obj, to_obj))
 +                      ret = error(_("Failed to copy notes from '%s' to '%s'"),
 +                                      sha1_to_hex(from_obj), sha1_to_hex(to_obj));
 +      }
 +
 +finish:
 +      finish_copy_notes_for_rewrite(c, msg);
 +      fclose(fp);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +/**
 + * Determines if the file looks like a piece of RFC2822 mail by grabbing all
 + * non-indented lines and checking if they look like they begin with valid
 + * header field names.
 + *
 + * Returns 1 if the file looks like a piece of mail, 0 otherwise.
 + */
 +static int is_mail(FILE *fp)
 +{
 +      const char *header_regex = "^[!-9;-~]+:";
 +      struct strbuf sb = STRBUF_INIT;
 +      regex_t regex;
 +      int ret = 1;
 +
 +      if (fseek(fp, 0L, SEEK_SET))
 +              die_errno(_("fseek failed"));
 +
 +      if (regcomp(&regex, header_regex, REG_NOSUB | REG_EXTENDED))
 +              die("invalid pattern: %s", header_regex);
 +
 +      while (!strbuf_getline_crlf(&sb, fp)) {
 +              if (!sb.len)
 +                      break; /* End of header */
 +
 +              /* Ignore indented folded lines */
 +              if (*sb.buf == '\t' || *sb.buf == ' ')
 +                      continue;
 +
 +              /* It's a header if it matches header_regex */
 +              if (regexec(&regex, sb.buf, 0, NULL, 0)) {
 +                      ret = 0;
 +                      goto done;
 +              }
 +      }
 +
 +done:
 +      regfree(&regex);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +/**
 + * Attempts to detect the patch_format of the patches contained in `paths`,
 + * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
 + * detection fails.
 + */
 +static int detect_patch_format(const char **paths)
 +{
 +      enum patch_format ret = PATCH_FORMAT_UNKNOWN;
 +      struct strbuf l1 = STRBUF_INIT;
 +      struct strbuf l2 = STRBUF_INIT;
 +      struct strbuf l3 = STRBUF_INIT;
 +      FILE *fp;
 +
 +      /*
 +       * We default to mbox format if input is from stdin and for directories
 +       */
 +      if (!*paths || !strcmp(*paths, "-") || is_directory(*paths))
 +              return PATCH_FORMAT_MBOX;
 +
 +      /*
 +       * Otherwise, check the first few lines of the first patch, starting
 +       * from the first non-blank line, to try to detect its format.
 +       */
 +
 +      fp = xfopen(*paths, "r");
 +
 +      while (!strbuf_getline_crlf(&l1, fp)) {
 +              if (l1.len)
 +                      break;
 +      }
 +
 +      if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) {
 +              ret = PATCH_FORMAT_MBOX;
 +              goto done;
 +      }
 +
 +      if (starts_with(l1.buf, "# This series applies on GIT commit")) {
 +              ret = PATCH_FORMAT_STGIT_SERIES;
 +              goto done;
 +      }
 +
 +      if (!strcmp(l1.buf, "# HG changeset patch")) {
 +              ret = PATCH_FORMAT_HG;
 +              goto done;
 +      }
 +
 +      strbuf_reset(&l2);
 +      strbuf_getline_crlf(&l2, fp);
 +      strbuf_reset(&l3);
 +      strbuf_getline_crlf(&l3, fp);
 +
 +      /*
 +       * If the second line is empty and the third is a From, Author or Date
 +       * entry, this is likely an StGit patch.
 +       */
 +      if (l1.len && !l2.len &&
 +              (starts_with(l3.buf, "From:") ||
 +               starts_with(l3.buf, "Author:") ||
 +               starts_with(l3.buf, "Date:"))) {
 +              ret = PATCH_FORMAT_STGIT;
 +              goto done;
 +      }
 +
 +      if (l1.len && is_mail(fp)) {
 +              ret = PATCH_FORMAT_MBOX;
 +              goto done;
 +      }
 +
 +done:
 +      fclose(fp);
 +      strbuf_release(&l1);
 +      return ret;
 +}
 +
 +/**
 + * Splits out individual email patches from `paths`, where each path is either
 + * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
 + */
 +static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      struct strbuf last = STRBUF_INIT;
 +
 +      cp.git_cmd = 1;
 +      argv_array_push(&cp.args, "mailsplit");
 +      argv_array_pushf(&cp.args, "-d%d", state->prec);
 +      argv_array_pushf(&cp.args, "-o%s", state->dir);
 +      argv_array_push(&cp.args, "-b");
 +      if (keep_cr)
 +              argv_array_push(&cp.args, "--keep-cr");
 +      argv_array_push(&cp.args, "--");
 +      argv_array_pushv(&cp.args, paths);
 +
 +      if (capture_command(&cp, &last, 8))
 +              return -1;
 +
 +      state->cur = 1;
 +      state->last = strtol(last.buf, NULL, 10);
 +
 +      return 0;
 +}
 +
 +/**
 + * Callback signature for split_mail_conv(). The foreign patch should be
 + * read from `in`, and the converted patch (in RFC2822 mail format) should be
 + * written to `out`. Return 0 on success, or -1 on failure.
 + */
 +typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr);
 +
 +/**
 + * Calls `fn` for each file in `paths` to convert the foreign patch to the
 + * RFC2822 mail format suitable for parsing with git-mailinfo.
 + *
 + * Returns 0 on success, -1 on failure.
 + */
 +static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
 +                      const char **paths, int keep_cr)
 +{
 +      static const char *stdin_only[] = {"-", NULL};
 +      int i;
 +
 +      if (!*paths)
 +              paths = stdin_only;
 +
 +      for (i = 0; *paths; paths++, i++) {
 +              FILE *in, *out;
 +              const char *mail;
 +              int ret;
 +
 +              if (!strcmp(*paths, "-"))
 +                      in = stdin;
 +              else
 +                      in = fopen(*paths, "r");
 +
 +              if (!in)
 +                      return error(_("could not open '%s' for reading: %s"),
 +                                      *paths, strerror(errno));
 +
 +              mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
 +
 +              out = fopen(mail, "w");
 +              if (!out)
 +                      return error(_("could not open '%s' for writing: %s"),
 +                                      mail, strerror(errno));
 +
 +              ret = fn(out, in, keep_cr);
 +
 +              fclose(out);
 +              fclose(in);
 +
 +              if (ret)
 +                      return error(_("could not parse patch '%s'"), *paths);
 +      }
 +
 +      state->cur = 1;
 +      state->last = i;
 +      return 0;
 +}
 +
 +/**
 + * A split_mail_conv() callback that converts an StGit patch to an RFC2822
 + * message suitable for parsing with git-mailinfo.
 + */
 +static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int subject_printed = 0;
 +
 +      while (!strbuf_getline(&sb, in, '\n')) {
 +              const char *str;
 +
 +              if (str_isspace(sb.buf))
 +                      continue;
 +              else if (skip_prefix(sb.buf, "Author:", &str))
 +                      fprintf(out, "From:%s\n", str);
 +              else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date"))
 +                      fprintf(out, "%s\n", sb.buf);
 +              else if (!subject_printed) {
 +                      fprintf(out, "Subject: %s\n", sb.buf);
 +                      subject_printed = 1;
 +              } else {
 +                      fprintf(out, "\n%s\n", sb.buf);
 +                      break;
 +              }
 +      }
 +
 +      strbuf_reset(&sb);
 +      while (strbuf_fread(&sb, 8192, in) > 0) {
 +              fwrite(sb.buf, 1, sb.len, out);
 +              strbuf_reset(&sb);
 +      }
 +
 +      strbuf_release(&sb);
 +      return 0;
 +}
 +
 +/**
 + * This function only supports a single StGit series file in `paths`.
 + *
 + * Given an StGit series file, converts the StGit patches in the series into
 + * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in
 + * the state directory.
 + *
 + * Returns 0 on success, -1 on failure.
 + */
 +static int split_mail_stgit_series(struct am_state *state, const char **paths,
 +                                      int keep_cr)
 +{
 +      const char *series_dir;
 +      char *series_dir_buf;
 +      FILE *fp;
 +      struct argv_array patches = ARGV_ARRAY_INIT;
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret;
 +
 +      if (!paths[0] || paths[1])
 +              return error(_("Only one StGIT patch series can be applied at once"));
 +
 +      series_dir_buf = xstrdup(*paths);
 +      series_dir = dirname(series_dir_buf);
 +
 +      fp = fopen(*paths, "r");
 +      if (!fp)
 +              return error(_("could not open '%s' for reading: %s"), *paths,
 +                              strerror(errno));
 +
 +      while (!strbuf_getline(&sb, fp, '\n')) {
 +              if (*sb.buf == '#')
 +                      continue; /* skip comment lines */
 +
 +              argv_array_push(&patches, mkpath("%s/%s", series_dir, sb.buf));
 +      }
 +
 +      fclose(fp);
 +      strbuf_release(&sb);
 +      free(series_dir_buf);
 +
 +      ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr);
 +
 +      argv_array_clear(&patches);
 +      return ret;
 +}
 +
 +/**
 + * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
 + * message suitable for parsing with git-mailinfo.
 + */
 +static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      while (!strbuf_getline(&sb, in, '\n')) {
 +              const char *str;
 +
 +              if (skip_prefix(sb.buf, "# User ", &str))
 +                      fprintf(out, "From: %s\n", str);
 +              else if (skip_prefix(sb.buf, "# Date ", &str)) {
 +                      unsigned long timestamp;
 +                      long tz, tz2;
 +                      char *end;
 +
 +                      errno = 0;
 +                      timestamp = strtoul(str, &end, 10);
 +                      if (errno)
 +                              return error(_("invalid timestamp"));
 +
 +                      if (!skip_prefix(end, " ", &str))
 +                              return error(_("invalid Date line"));
 +
 +                      errno = 0;
 +                      tz = strtol(str, &end, 10);
 +                      if (errno)
 +                              return error(_("invalid timezone offset"));
 +
 +                      if (*end)
 +                              return error(_("invalid Date line"));
 +
 +                      /*
 +                       * mercurial's timezone is in seconds west of UTC,
 +                       * however git's timezone is in hours + minutes east of
 +                       * UTC. Convert it.
 +                       */
 +                      tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
 +                      if (tz > 0)
 +                              tz2 = -tz2;
 +
 +                      fprintf(out, "Date: %s\n", show_date(timestamp, tz2, DATE_MODE(RFC2822)));
 +              } else if (starts_with(sb.buf, "# ")) {
 +                      continue;
 +              } else {
 +                      fprintf(out, "\n%s\n", sb.buf);
 +                      break;
 +              }
 +      }
 +
 +      strbuf_reset(&sb);
 +      while (strbuf_fread(&sb, 8192, in) > 0) {
 +              fwrite(sb.buf, 1, sb.len, out);
 +              strbuf_reset(&sb);
 +      }
 +
 +      strbuf_release(&sb);
 +      return 0;
 +}
 +
 +/**
 + * Splits a list of files/directories into individual email patches. Each path
 + * in `paths` must be a file/directory that is formatted according to
 + * `patch_format`.
 + *
 + * Once split out, the individual email patches will be stored in the state
 + * directory, with each patch's filename being its index, padded to state->prec
 + * digits.
 + *
 + * state->cur will be set to the index of the first mail, and state->last will
 + * be set to the index of the last mail.
 + *
 + * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
 + * to disable this behavior, -1 to use the default configured setting.
 + *
 + * Returns 0 on success, -1 on failure.
 + */
 +static int split_mail(struct am_state *state, enum patch_format patch_format,
 +                      const char **paths, int keep_cr)
 +{
 +      if (keep_cr < 0) {
 +              keep_cr = 0;
 +              git_config_get_bool("am.keepcr", &keep_cr);
 +      }
 +
 +      switch (patch_format) {
 +      case PATCH_FORMAT_MBOX:
 +              return split_mail_mbox(state, paths, keep_cr);
 +      case PATCH_FORMAT_STGIT:
 +              return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
 +      case PATCH_FORMAT_STGIT_SERIES:
 +              return split_mail_stgit_series(state, paths, keep_cr);
 +      case PATCH_FORMAT_HG:
 +              return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
 +      default:
 +              die("BUG: invalid patch_format");
 +      }
 +      return -1;
 +}
 +
 +/**
 + * Setup a new am session for applying patches
 + */
 +static void am_setup(struct am_state *state, enum patch_format patch_format,
 +                      const char **paths, int keep_cr)
 +{
 +      unsigned char curr_head[GIT_SHA1_RAWSZ];
 +      const char *str;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      if (!patch_format)
 +              patch_format = detect_patch_format(paths);
 +
 +      if (!patch_format) {
 +              fprintf_ln(stderr, _("Patch format detection failed."));
 +              exit(128);
 +      }
 +
 +      if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
 +              die_errno(_("failed to create directory '%s'"), state->dir);
 +
 +      if (split_mail(state, patch_format, paths, keep_cr) < 0) {
 +              am_destroy(state);
 +              die(_("Failed to split patches."));
 +      }
 +
 +      if (state->rebasing)
 +              state->threeway = 1;
 +
 +      write_file(am_path(state, "threeway"), 1, state->threeway ? "t" : "f");
 +
 +      write_file(am_path(state, "quiet"), 1, state->quiet ? "t" : "f");
 +
 +      write_file(am_path(state, "sign"), 1, state->signoff ? "t" : "f");
 +
 +      write_file(am_path(state, "utf8"), 1, state->utf8 ? "t" : "f");
 +
 +      switch (state->keep) {
 +      case KEEP_FALSE:
 +              str = "f";
 +              break;
 +      case KEEP_TRUE:
 +              str = "t";
 +              break;
 +      case KEEP_NON_PATCH:
 +              str = "b";
 +              break;
 +      default:
 +              die("BUG: invalid value for state->keep");
 +      }
 +
 +      write_file(am_path(state, "keep"), 1, "%s", str);
 +
 +      write_file(am_path(state, "messageid"), 1, state->message_id ? "t" : "f");
 +
 +      switch (state->scissors) {
 +      case SCISSORS_UNSET:
 +              str = "";
 +              break;
 +      case SCISSORS_FALSE:
 +              str = "f";
 +              break;
 +      case SCISSORS_TRUE:
 +              str = "t";
 +              break;
 +      default:
 +              die("BUG: invalid value for state->scissors");
 +      }
 +
 +      write_file(am_path(state, "scissors"), 1, "%s", str);
 +
 +      sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
 +      write_file(am_path(state, "apply-opt"), 1, "%s", sb.buf);
 +
 +      if (state->rebasing)
 +              write_file(am_path(state, "rebasing"), 1, "%s", "");
 +      else
 +              write_file(am_path(state, "applying"), 1, "%s", "");
 +
 +      if (!get_sha1("HEAD", curr_head)) {
 +              write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(curr_head));
 +              if (!state->rebasing)
 +                      update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
 +                                      UPDATE_REFS_DIE_ON_ERR);
 +      } else {
 +              write_file(am_path(state, "abort-safety"), 1, "%s", "");
 +              if (!state->rebasing)
 +                      delete_ref("ORIG_HEAD", NULL, 0);
 +      }
 +
 +      /*
 +       * NOTE: Since the "next" and "last" files determine if an am_state
 +       * session is in progress, they should be written last.
 +       */
 +
 +      write_file(am_path(state, "next"), 1, "%d", state->cur);
 +
 +      write_file(am_path(state, "last"), 1, "%d", state->last);
 +
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Increments the patch pointer, and cleans am_state for the application of the
 + * next patch.
 + */
 +static void am_next(struct am_state *state)
 +{
 +      unsigned char head[GIT_SHA1_RAWSZ];
 +
 +      free(state->author_name);
 +      state->author_name = NULL;
 +
 +      free(state->author_email);
 +      state->author_email = NULL;
 +
 +      free(state->author_date);
 +      state->author_date = NULL;
 +
 +      free(state->msg);
 +      state->msg = NULL;
 +      state->msg_len = 0;
 +
 +      unlink(am_path(state, "author-script"));
 +      unlink(am_path(state, "final-commit"));
 +
 +      hashclr(state->orig_commit);
 +      unlink(am_path(state, "original-commit"));
 +
 +      if (!get_sha1("HEAD", head))
 +              write_file(am_path(state, "abort-safety"), 1, "%s", sha1_to_hex(head));
 +      else
 +              write_file(am_path(state, "abort-safety"), 1, "%s", "");
 +
 +      state->cur++;
 +      write_file(am_path(state, "next"), 1, "%d", state->cur);
 +}
 +
 +/**
 + * Returns the filename of the current patch email.
 + */
 +static const char *msgnum(const struct am_state *state)
 +{
 +      static struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_reset(&sb);
 +      strbuf_addf(&sb, "%0*d", state->prec, state->cur);
 +
 +      return sb.buf;
 +}
 +
 +/**
 + * Refresh and write index.
 + */
 +static void refresh_and_write_cache(void)
 +{
 +      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +
 +      hold_locked_index(lock_file, 1);
 +      refresh_cache(REFRESH_QUIET);
 +      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +              die(_("unable to write index file"));
 +}
 +
 +/**
 + * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 + * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 + * strbuf is provided, the space-separated list of files that differ will be
 + * appended to it.
 + */
 +static int index_has_changes(struct strbuf *sb)
 +{
 +      unsigned char head[GIT_SHA1_RAWSZ];
 +      int i;
 +
 +      if (!get_sha1_tree("HEAD", head)) {
 +              struct diff_options opt;
 +
 +              diff_setup(&opt);
 +              DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
 +              if (!sb)
 +                      DIFF_OPT_SET(&opt, QUICK);
 +              do_diff_cache(head, &opt);
 +              diffcore_std(&opt);
 +              for (i = 0; sb && i < diff_queued_diff.nr; i++) {
 +                      if (i)
 +                              strbuf_addch(sb, ' ');
 +                      strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
 +              }
 +              diff_flush(&opt);
 +              return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
 +      } else {
 +              for (i = 0; sb && i < active_nr; i++) {
 +                      if (i)
 +                              strbuf_addch(sb, ' ');
 +                      strbuf_addstr(sb, active_cache[i]->name);
 +              }
 +              return !!active_nr;
 +      }
 +}
 +
 +/**
 + * Dies with a user-friendly message on how to proceed after resolving the
 + * problem. This message can be overridden with state->resolvemsg.
 + */
 +static void NORETURN die_user_resolve(const struct am_state *state)
 +{
 +      if (state->resolvemsg) {
 +              printf_ln("%s", state->resolvemsg);
 +      } else {
 +              const char *cmdline = state->interactive ? "git am -i" : "git am";
 +
 +              printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
 +              printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
 +              printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
 +      }
 +
 +      exit(128);
 +}
 +
 +/**
 + * Appends signoff to the "msg" field of the am_state.
 + */
 +static void am_append_signoff(struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
 +      append_signoff(&sb, 0, 0);
 +      state->msg = strbuf_detach(&sb, &state->msg_len);
 +}
 +
 +/**
 + * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
 + * state->msg will be set to the patch message. state->author_name,
 + * state->author_email and state->author_date will be set to the patch author's
 + * name, email and date respectively. The patch body will be written to the
 + * state directory's "patch" file.
 + *
 + * Returns 1 if the patch should be skipped, 0 otherwise.
 + */
 +static int parse_mail(struct am_state *state, const char *mail)
 +{
 +      FILE *fp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      struct strbuf sb = STRBUF_INIT;
 +      struct strbuf msg = STRBUF_INIT;
 +      struct strbuf author_name = STRBUF_INIT;
 +      struct strbuf author_date = STRBUF_INIT;
 +      struct strbuf author_email = STRBUF_INIT;
 +      int ret = 0;
 +
 +      cp.git_cmd = 1;
 +      cp.in = xopen(mail, O_RDONLY, 0);
 +      cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
 +
 +      argv_array_push(&cp.args, "mailinfo");
 +      argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
 +
 +      switch (state->keep) {
 +      case KEEP_FALSE:
 +              break;
 +      case KEEP_TRUE:
 +              argv_array_push(&cp.args, "-k");
 +              break;
 +      case KEEP_NON_PATCH:
 +              argv_array_push(&cp.args, "-b");
 +              break;
 +      default:
 +              die("BUG: invalid value for state->keep");
 +      }
 +
 +      if (state->message_id)
 +              argv_array_push(&cp.args, "-m");
 +
 +      switch (state->scissors) {
 +      case SCISSORS_UNSET:
 +              break;
 +      case SCISSORS_FALSE:
 +              argv_array_push(&cp.args, "--no-scissors");
 +              break;
 +      case SCISSORS_TRUE:
 +              argv_array_push(&cp.args, "--scissors");
 +              break;
 +      default:
 +              die("BUG: invalid value for state->scissors");
 +      }
 +
 +      argv_array_push(&cp.args, am_path(state, "msg"));
 +      argv_array_push(&cp.args, am_path(state, "patch"));
 +
 +      if (run_command(&cp) < 0)
 +              die("could not parse patch");
 +
 +      close(cp.in);
 +      close(cp.out);
 +
 +      /* Extract message and author information */
 +      fp = xfopen(am_path(state, "info"), "r");
 +      while (!strbuf_getline(&sb, fp, '\n')) {
 +              const char *x;
 +
 +              if (skip_prefix(sb.buf, "Subject: ", &x)) {
 +                      if (msg.len)
 +                              strbuf_addch(&msg, '\n');
 +                      strbuf_addstr(&msg, x);
 +              } else if (skip_prefix(sb.buf, "Author: ", &x))
 +                      strbuf_addstr(&author_name, x);
 +              else if (skip_prefix(sb.buf, "Email: ", &x))
 +                      strbuf_addstr(&author_email, x);
 +              else if (skip_prefix(sb.buf, "Date: ", &x))
 +                      strbuf_addstr(&author_date, x);
 +      }
 +      fclose(fp);
 +
 +      /* Skip pine's internal folder data */
 +      if (!strcmp(author_name.buf, "Mail System Internal Data")) {
 +              ret = 1;
 +              goto finish;
 +      }
 +
 +      if (is_empty_file(am_path(state, "patch"))) {
 +              printf_ln(_("Patch is empty. Was it split wrong?"));
 +              die_user_resolve(state);
 +      }
 +
 +      strbuf_addstr(&msg, "\n\n");
 +      if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
 +              die_errno(_("could not read '%s'"), am_path(state, "msg"));
 +      stripspace(&msg, 0);
 +
 +      if (state->signoff)
 +              append_signoff(&msg, 0, 0);
 +
 +      assert(!state->author_name);
 +      state->author_name = strbuf_detach(&author_name, NULL);
 +
 +      assert(!state->author_email);
 +      state->author_email = strbuf_detach(&author_email, NULL);
 +
 +      assert(!state->author_date);
 +      state->author_date = strbuf_detach(&author_date, NULL);
 +
 +      assert(!state->msg);
 +      state->msg = strbuf_detach(&msg, &state->msg_len);
 +
 +finish:
 +      strbuf_release(&msg);
 +      strbuf_release(&author_date);
 +      strbuf_release(&author_email);
 +      strbuf_release(&author_name);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +/**
 + * Sets commit_id to the commit hash where the mail was generated from.
 + * Returns 0 on success, -1 on failure.
 + */
 +static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      FILE *fp = xfopen(mail, "r");
 +      const char *x;
 +
 +      if (strbuf_getline(&sb, fp, '\n'))
 +              return -1;
 +
 +      if (!skip_prefix(sb.buf, "From ", &x))
 +              return -1;
 +
 +      if (get_sha1_hex(x, commit_id) < 0)
 +              return -1;
 +
 +      strbuf_release(&sb);
 +      fclose(fp);
 +      return 0;
 +}
 +
 +/**
 + * Sets state->msg, state->author_name, state->author_email, state->author_date
 + * to the commit's respective info.
 + */
 +static void get_commit_info(struct am_state *state, struct commit *commit)
 +{
 +      const char *buffer, *ident_line, *author_date, *msg;
 +      size_t ident_len;
 +      struct ident_split ident_split;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
 +
 +      ident_line = find_commit_header(buffer, "author", &ident_len);
 +
 +      if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
 +              strbuf_add(&sb, ident_line, ident_len);
 +              die(_("invalid ident line: %s"), sb.buf);
 +      }
 +
 +      assert(!state->author_name);
 +      if (ident_split.name_begin) {
 +              strbuf_add(&sb, ident_split.name_begin,
 +                      ident_split.name_end - ident_split.name_begin);
 +              state->author_name = strbuf_detach(&sb, NULL);
 +      } else
 +              state->author_name = xstrdup("");
 +
 +      assert(!state->author_email);
 +      if (ident_split.mail_begin) {
 +              strbuf_add(&sb, ident_split.mail_begin,
 +                      ident_split.mail_end - ident_split.mail_begin);
 +              state->author_email = strbuf_detach(&sb, NULL);
 +      } else
 +              state->author_email = xstrdup("");
 +
 +      author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
 +      strbuf_addstr(&sb, author_date);
 +      assert(!state->author_date);
 +      state->author_date = strbuf_detach(&sb, NULL);
 +
 +      assert(!state->msg);
 +      msg = strstr(buffer, "\n\n");
 +      if (!msg)
 +              die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1));
 +      state->msg = xstrdup(msg + 2);
 +      state->msg_len = strlen(state->msg);
 +}
 +
 +/**
 + * Writes `commit` as a patch to the state directory's "patch" file.
 + */
 +static void write_commit_patch(const struct am_state *state, struct commit *commit)
 +{
 +      struct rev_info rev_info;
 +      FILE *fp;
 +
 +      fp = xfopen(am_path(state, "patch"), "w");
 +      init_revisions(&rev_info, NULL);
 +      rev_info.diff = 1;
 +      rev_info.abbrev = 0;
 +      rev_info.disable_stdin = 1;
 +      rev_info.show_root_diff = 1;
 +      rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
 +      rev_info.no_commit_id = 1;
 +      DIFF_OPT_SET(&rev_info.diffopt, BINARY);
 +      DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
 +      rev_info.diffopt.use_color = 0;
 +      rev_info.diffopt.file = fp;
 +      rev_info.diffopt.close_file = 1;
 +      add_pending_object(&rev_info, &commit->object, "");
 +      diff_setup_done(&rev_info.diffopt);
 +      log_tree_commit(&rev_info, commit);
 +}
 +
 +/**
 + * Writes the diff of the index against HEAD as a patch to the state
 + * directory's "patch" file.
 + */
 +static void write_index_patch(const struct am_state *state)
 +{
 +      struct tree *tree;
 +      unsigned char head[GIT_SHA1_RAWSZ];
 +      struct rev_info rev_info;
 +      FILE *fp;
 +
 +      if (!get_sha1_tree("HEAD", head))
 +              tree = lookup_tree(head);
 +      else
 +              tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
 +
 +      fp = xfopen(am_path(state, "patch"), "w");
 +      init_revisions(&rev_info, NULL);
 +      rev_info.diff = 1;
 +      rev_info.disable_stdin = 1;
 +      rev_info.no_commit_id = 1;
 +      rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
 +      rev_info.diffopt.use_color = 0;
 +      rev_info.diffopt.file = fp;
 +      rev_info.diffopt.close_file = 1;
 +      add_pending_object(&rev_info, &tree->object, "");
 +      diff_setup_done(&rev_info.diffopt);
 +      run_diff_index(&rev_info, 1);
 +}
 +
 +/**
 + * Like parse_mail(), but parses the mail by looking up its commit ID
 + * directly. This is used in --rebasing mode to bypass git-mailinfo's munging
 + * of patches.
 + *
 + * state->orig_commit will be set to the original commit ID.
 + *
 + * Will always return 0 as the patch should never be skipped.
 + */
 +static int parse_mail_rebase(struct am_state *state, const char *mail)
 +{
 +      struct commit *commit;
 +      unsigned char commit_sha1[GIT_SHA1_RAWSZ];
 +
 +      if (get_mail_commit_sha1(commit_sha1, mail) < 0)
 +              die(_("could not parse %s"), mail);
 +
 +      commit = lookup_commit_or_die(commit_sha1, mail);
 +
 +      get_commit_info(state, commit);
 +
 +      write_commit_patch(state, commit);
 +
 +      hashcpy(state->orig_commit, commit_sha1);
 +      write_file(am_path(state, "original-commit"), 1, "%s",
 +                      sha1_to_hex(commit_sha1));
 +
 +      return 0;
 +}
 +
 +/**
 + * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
 + * `index_file` is not NULL, the patch will be applied to that index.
 + */
 +static int run_apply(const struct am_state *state, const char *index_file)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +
 +      cp.git_cmd = 1;
 +
 +      if (index_file)
 +              argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
 +
 +      /*
 +       * If we are allowed to fall back on 3-way merge, don't give false
 +       * errors during the initial attempt.
 +       */
 +      if (state->threeway && !index_file) {
 +              cp.no_stdout = 1;
 +              cp.no_stderr = 1;
 +      }
 +
 +      argv_array_push(&cp.args, "apply");
 +
 +      argv_array_pushv(&cp.args, state->git_apply_opts.argv);
 +
 +      if (index_file)
 +              argv_array_push(&cp.args, "--cached");
 +      else
 +              argv_array_push(&cp.args, "--index");
 +
 +      argv_array_push(&cp.args, am_path(state, "patch"));
 +
 +      if (run_command(&cp))
 +              return -1;
 +
 +      /* Reload index as git-apply will have modified it. */
 +      discard_cache();
 +      read_cache_from(index_file ? index_file : get_index_file());
 +
 +      return 0;
 +}
 +
 +/**
 + * Builds an index that contains just the blobs needed for a 3way merge.
 + */
 +static int build_fake_ancestor(const struct am_state *state, const char *index_file)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +
 +      cp.git_cmd = 1;
 +      argv_array_push(&cp.args, "apply");
 +      argv_array_pushv(&cp.args, state->git_apply_opts.argv);
 +      argv_array_pushf(&cp.args, "--build-fake-ancestor=%s", index_file);
 +      argv_array_push(&cp.args, am_path(state, "patch"));
 +
 +      if (run_command(&cp))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +/**
 + * Attempt a threeway merge, using index_path as the temporary index.
 + */
 +static int fall_back_threeway(const struct am_state *state, const char *index_path)
 +{
 +      unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
 +                    our_tree[GIT_SHA1_RAWSZ];
 +      const unsigned char *bases[1] = {orig_tree};
 +      struct merge_options o;
 +      struct commit *result;
 +      char *his_tree_name;
 +
 +      if (get_sha1("HEAD", our_tree) < 0)
 +              hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
 +
 +      if (build_fake_ancestor(state, index_path))
 +              return error("could not build fake ancestor");
 +
 +      discard_cache();
 +      read_cache_from(index_path);
 +
 +      if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
 +              return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
 +
 +      say(state, stdout, _("Using index info to reconstruct a base tree..."));
 +
 +      if (!state->quiet) {
 +              /*
 +               * List paths that needed 3-way fallback, so that the user can
 +               * review them with extra care to spot mismerges.
 +               */
 +              struct rev_info rev_info;
 +              const char *diff_filter_str = "--diff-filter=AM";
 +
 +              init_revisions(&rev_info, NULL);
 +              rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
 +              diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1);
 +              add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
 +              diff_setup_done(&rev_info.diffopt);
 +              run_diff_index(&rev_info, 1);
 +      }
 +
 +      if (run_apply(state, index_path))
 +              return error(_("Did you hand edit your patch?\n"
 +                              "It does not apply to blobs recorded in its index."));
 +
 +      if (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))
 +              return error("could not write tree");
 +
 +      say(state, stdout, _("Falling back to patching base and 3-way merge..."));
 +
 +      discard_cache();
 +      read_cache();
 +
 +      /*
 +       * This is not so wrong. Depending on which base we picked, orig_tree
 +       * may be wildly different from ours, but his_tree has the same set of
 +       * wildly different changes in parts the patch did not touch, so
 +       * recursive ends up canceling them, saying that we reverted all those
 +       * changes.
 +       */
 +
 +      init_merge_options(&o);
 +
 +      o.branch1 = "HEAD";
 +      his_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
 +      o.branch2 = his_tree_name;
 +
 +      if (state->quiet)
 +              o.verbosity = 0;
 +
 +      if (merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result)) {
 +              rerere(state->allow_rerere_autoupdate);
 +              free(his_tree_name);
 +              return error(_("Failed to merge in the changes."));
 +      }
 +
 +      free(his_tree_name);
 +      return 0;
 +}
 +
 +/**
 + * Commits the current index with state->msg as the commit message and
 + * state->author_name, state->author_email and state->author_date as the author
 + * information.
 + */
 +static void do_commit(const struct am_state *state)
 +{
 +      unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
 +                    commit[GIT_SHA1_RAWSZ];
 +      unsigned char *ptr;
 +      struct commit_list *parents = NULL;
 +      const char *reflog_msg, *author;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      if (run_hook_le(NULL, "pre-applypatch", NULL))
 +              exit(1);
 +
 +      if (write_cache_as_tree(tree, 0, NULL))
 +              die(_("git write-tree failed to write a tree"));
 +
 +      if (!get_sha1_commit("HEAD", parent)) {
 +              ptr = parent;
 +              commit_list_insert(lookup_commit(parent), &parents);
 +      } else {
 +              ptr = NULL;
 +              say(state, stderr, _("applying to an empty history"));
 +      }
 +
 +      author = fmt_ident(state->author_name, state->author_email,
 +                      state->ignore_date ? NULL : state->author_date,
 +                      IDENT_STRICT);
 +
 +      if (state->committer_date_is_author_date)
 +              setenv("GIT_COMMITTER_DATE",
 +                      state->ignore_date ? "" : state->author_date, 1);
 +
 +      if (commit_tree(state->msg, state->msg_len, tree, parents, commit,
 +                              author, state->sign_commit))
 +              die(_("failed to write commit object"));
 +
 +      reflog_msg = getenv("GIT_REFLOG_ACTION");
 +      if (!reflog_msg)
 +              reflog_msg = "am";
 +
 +      strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
 +                      state->msg);
 +
 +      update_ref(sb.buf, "HEAD", commit, ptr, 0, UPDATE_REFS_DIE_ON_ERR);
 +
 +      if (state->rebasing) {
 +              FILE *fp = xfopen(am_path(state, "rewritten"), "a");
 +
 +              assert(!is_null_sha1(state->orig_commit));
 +              fprintf(fp, "%s ", sha1_to_hex(state->orig_commit));
 +              fprintf(fp, "%s\n", sha1_to_hex(commit));
 +              fclose(fp);
 +      }
 +
 +      run_hook_le(NULL, "post-applypatch", NULL);
 +
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Validates the am_state for resuming -- the "msg" and authorship fields must
 + * be filled up.
 + */
 +static void validate_resume_state(const struct am_state *state)
 +{
 +      if (!state->msg)
 +              die(_("cannot resume: %s does not exist."),
 +                      am_path(state, "final-commit"));
 +
 +      if (!state->author_name || !state->author_email || !state->author_date)
 +              die(_("cannot resume: %s does not exist."),
 +                      am_path(state, "author-script"));
 +}
 +
 +/**
 + * Interactively prompt the user on whether the current patch should be
 + * applied.
 + *
 + * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
 + * skip it.
 + */
 +static int do_interactive(struct am_state *state)
 +{
 +      assert(state->msg);
 +
 +      if (!isatty(0))
 +              die(_("cannot be interactive without stdin connected to a terminal."));
 +
 +      for (;;) {
 +              const char *reply;
 +
 +              puts(_("Commit Body is:"));
 +              puts("--------------------------");
 +              printf("%s", state->msg);
 +              puts("--------------------------");
 +
 +              /*
 +               * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 +               * in your translation. The program will only accept English
 +               * input at this point.
 +               */
 +              reply = git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);
 +
 +              if (!reply) {
 +                      continue;
 +              } else if (*reply == 'y' || *reply == 'Y') {
 +                      return 0;
 +              } else if (*reply == 'a' || *reply == 'A') {
 +                      state->interactive = 0;
 +                      return 0;
 +              } else if (*reply == 'n' || *reply == 'N') {
 +                      return 1;
 +              } else if (*reply == 'e' || *reply == 'E') {
 +                      struct strbuf msg = STRBUF_INIT;
 +
 +                      if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) {
 +                              free(state->msg);
 +                              state->msg = strbuf_detach(&msg, &state->msg_len);
 +                      }
 +                      strbuf_release(&msg);
 +              } else if (*reply == 'v' || *reply == 'V') {
 +                      const char *pager = git_pager(1);
 +                      struct child_process cp = CHILD_PROCESS_INIT;
 +
 +                      if (!pager)
 +                              pager = "cat";
 +                      argv_array_push(&cp.args, pager);
 +                      argv_array_push(&cp.args, am_path(state, "patch"));
 +                      run_command(&cp);
 +              }
 +      }
 +}
 +
 +/**
 + * Applies all queued mail.
 + *
 + * If `resume` is true, we are "resuming". The "msg" and authorship fields, as
 + * well as the state directory's "patch" file is used as-is for applying the
 + * patch and committing it.
 + */
 +static void am_run(struct am_state *state, int resume)
 +{
 +      const char *argv_gc_auto[] = {"gc", "--auto", NULL};
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      unlink(am_path(state, "dirtyindex"));
 +
 +      refresh_and_write_cache();
 +
 +      if (index_has_changes(&sb)) {
 +              write_file(am_path(state, "dirtyindex"), 1, "t");
 +              die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
 +      }
 +
 +      strbuf_release(&sb);
 +
 +      while (state->cur <= state->last) {
 +              const char *mail = am_path(state, msgnum(state));
 +              int apply_status;
 +
 +              if (!file_exists(mail))
 +                      goto next;
 +
 +              if (resume) {
 +                      validate_resume_state(state);
 +              } else {
 +                      int skip;
 +
 +                      if (state->rebasing)
 +                              skip = parse_mail_rebase(state, mail);
 +                      else
 +                              skip = parse_mail(state, mail);
 +
 +                      if (skip)
 +                              goto next; /* mail should be skipped */
 +
 +                      write_author_script(state);
 +                      write_commit_msg(state);
 +              }
 +
 +              if (state->interactive && do_interactive(state))
 +                      goto next;
 +
 +              if (run_applypatch_msg_hook(state))
 +                      exit(1);
 +
 +              say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 +
 +              apply_status = run_apply(state, NULL);
 +
 +              if (apply_status && state->threeway) {
 +                      struct strbuf sb = STRBUF_INIT;
 +
 +                      strbuf_addstr(&sb, am_path(state, "patch-merge-index"));
 +                      apply_status = fall_back_threeway(state, sb.buf);
 +                      strbuf_release(&sb);
 +
 +                      /*
 +                       * Applying the patch to an earlier tree and merging
 +                       * the result may have produced the same tree as ours.
 +                       */
 +                      if (!apply_status && !index_has_changes(NULL)) {
 +                              say(state, stdout, _("No changes -- Patch already applied."));
 +                              goto next;
 +                      }
 +              }
 +
 +              if (apply_status) {
 +                      int advice_amworkdir = 1;
 +
 +                      printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
 +                              linelen(state->msg), state->msg);
 +
 +                      git_config_get_bool("advice.amworkdir", &advice_amworkdir);
 +
 +                      if (advice_amworkdir)
 +                              printf_ln(_("The copy of the patch that failed is found in: %s"),
 +                                              am_path(state, "patch"));
 +
 +                      die_user_resolve(state);
 +              }
 +
 +              do_commit(state);
 +
 +next:
 +              am_next(state);
 +
 +              if (resume)
 +                      am_load(state);
 +              resume = 0;
 +      }
 +
 +      if (!is_empty_file(am_path(state, "rewritten"))) {
 +              assert(state->rebasing);
 +              copy_notes_for_rebase(state);
 +              run_post_rewrite_hook(state);
 +      }
 +
 +      /*
 +       * In rebasing mode, it's up to the caller to take care of
 +       * housekeeping.
 +       */
 +      if (!state->rebasing) {
 +              am_destroy(state);
 +              run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 +      }
 +}
 +
 +/**
 + * Resume the current am session after patch application failure. The user did
 + * all the hard work, and we do not have to do any patch application. Just
 + * trust and commit what the user has in the index and working tree.
 + */
 +static void am_resolve(struct am_state *state)
 +{
 +      validate_resume_state(state);
 +
 +      say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 +
 +      if (!index_has_changes(NULL)) {
 +              printf_ln(_("No changes - did you forget to use 'git add'?\n"
 +                      "If there is nothing left to stage, chances are that something else\n"
 +                      "already introduced the same changes; you might want to skip this patch."));
 +              die_user_resolve(state);
 +      }
 +
 +      if (unmerged_cache()) {
 +              printf_ln(_("You still have unmerged paths in your index.\n"
 +                      "Did you forget to use 'git add'?"));
 +              die_user_resolve(state);
 +      }
 +
 +      if (state->interactive) {
 +              write_index_patch(state);
 +              if (do_interactive(state))
 +                      goto next;
 +      }
 +
 +      rerere(0);
 +
 +      do_commit(state);
 +
 +next:
 +      am_next(state);
 +      am_load(state);
 +      am_run(state, 0);
 +}
 +
 +/**
 + * Performs a checkout fast-forward from `head` to `remote`. If `reset` is
 + * true, any unmerged entries will be discarded. Returns 0 on success, -1 on
 + * failure.
 + */
 +static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
 +{
 +      struct lock_file *lock_file;
 +      struct unpack_trees_options opts;
 +      struct tree_desc t[2];
 +
 +      if (parse_tree(head) || parse_tree(remote))
 +              return -1;
 +
 +      lock_file = xcalloc(1, sizeof(struct lock_file));
 +      hold_locked_index(lock_file, 1);
 +
 +      refresh_cache(REFRESH_QUIET);
 +
 +      memset(&opts, 0, sizeof(opts));
 +      opts.head_idx = 1;
 +      opts.src_index = &the_index;
 +      opts.dst_index = &the_index;
 +      opts.update = 1;
 +      opts.merge = 1;
 +      opts.reset = reset;
 +      opts.fn = twoway_merge;
 +      init_tree_desc(&t[0], head->buffer, head->size);
 +      init_tree_desc(&t[1], remote->buffer, remote->size);
 +
 +      if (unpack_trees(2, t, &opts)) {
 +              rollback_lock_file(lock_file);
 +              return -1;
 +      }
 +
 +      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +              die(_("unable to write new index file"));
 +
 +      return 0;
 +}
 +
 +/**
 + * Clean the index without touching entries that are not modified between
 + * `head` and `remote`.
 + */
 +static int clean_index(const unsigned char *head, const unsigned char *remote)
 +{
 +      struct lock_file *lock_file;
 +      struct tree *head_tree, *remote_tree, *index_tree;
 +      unsigned char index[GIT_SHA1_RAWSZ];
 +      struct pathspec pathspec;
 +
 +      head_tree = parse_tree_indirect(head);
 +      if (!head_tree)
 +              return error(_("Could not parse object '%s'."), sha1_to_hex(head));
 +
 +      remote_tree = parse_tree_indirect(remote);
 +      if (!remote_tree)
 +              return error(_("Could not parse object '%s'."), sha1_to_hex(remote));
 +
 +      read_cache_unmerged();
 +
 +      if (fast_forward_to(head_tree, head_tree, 1))
 +              return -1;
 +
 +      if (write_cache_as_tree(index, 0, NULL))
 +              return -1;
 +
 +      index_tree = parse_tree_indirect(index);
 +      if (!index_tree)
 +              return error(_("Could not parse object '%s'."), sha1_to_hex(index));
 +
 +      if (fast_forward_to(index_tree, remote_tree, 0))
 +              return -1;
 +
 +      memset(&pathspec, 0, sizeof(pathspec));
 +
 +      lock_file = xcalloc(1, sizeof(struct lock_file));
 +      hold_locked_index(lock_file, 1);
 +
 +      if (read_tree(remote_tree, 0, &pathspec)) {
 +              rollback_lock_file(lock_file);
 +              return -1;
 +      }
 +
 +      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +              die(_("unable to write new index file"));
 +
 +      remove_branch_state();
 +
 +      return 0;
 +}
 +
 +/**
 + * Resets rerere's merge resolution metadata.
 + */
 +static void am_rerere_clear(void)
 +{
 +      struct string_list merge_rr = STRING_LIST_INIT_DUP;
 +      int fd = setup_rerere(&merge_rr, 0);
 +
 +      if (fd < 0)
 +              return;
 +
 +      rerere_clear(&merge_rr);
 +      string_list_clear(&merge_rr, 1);
 +}
 +
 +/**
 + * Resume the current am session by skipping the current patch.
 + */
 +static void am_skip(struct am_state *state)
 +{
 +      unsigned char head[GIT_SHA1_RAWSZ];
 +
 +      am_rerere_clear();
 +
 +      if (get_sha1("HEAD", head))
 +              hashcpy(head, EMPTY_TREE_SHA1_BIN);
 +
 +      if (clean_index(head, head))
 +              die(_("failed to clean index"));
 +
 +      am_next(state);
 +      am_load(state);
 +      am_run(state, 0);
 +}
 +
 +/**
 + * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
 + *
 + * It is not safe to reset HEAD when:
 + * 1. git-am previously failed because the index was dirty.
 + * 2. HEAD has moved since git-am previously failed.
 + */
 +static int safe_to_abort(const struct am_state *state)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
 +
 +      if (file_exists(am_path(state, "dirtyindex")))
 +              return 0;
 +
 +      if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
 +              if (get_sha1_hex(sb.buf, abort_safety))
 +                      die(_("could not parse %s"), am_path(state, "abort_safety"));
 +      } else
 +              hashclr(abort_safety);
 +
 +      if (get_sha1("HEAD", head))
 +              hashclr(head);
 +
 +      if (!hashcmp(head, abort_safety))
 +              return 1;
 +
 +      error(_("You seem to have moved HEAD since the last 'am' failure.\n"
 +              "Not rewinding to ORIG_HEAD"));
 +
 +      return 0;
 +}
 +
 +/**
 + * Aborts the current am session if it is safe to do so.
 + */
 +static void am_abort(struct am_state *state)
 +{
 +      unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
 +      int has_curr_head, has_orig_head;
 +      char *curr_branch;
 +
 +      if (!safe_to_abort(state)) {
 +              am_destroy(state);
 +              return;
 +      }
 +
 +      am_rerere_clear();
 +
 +      curr_branch = resolve_refdup("HEAD", 0, curr_head, NULL);
 +      has_curr_head = !is_null_sha1(curr_head);
 +      if (!has_curr_head)
 +              hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
 +
 +      has_orig_head = !get_sha1("ORIG_HEAD", orig_head);
 +      if (!has_orig_head)
 +              hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
 +
 +      clean_index(curr_head, orig_head);
 +
 +      if (has_orig_head)
 +              update_ref("am --abort", "HEAD", orig_head,
 +                              has_curr_head ? curr_head : NULL, 0,
 +                              UPDATE_REFS_DIE_ON_ERR);
 +      else if (curr_branch)
 +              delete_ref(curr_branch, NULL, REF_NODEREF);
 +
 +      free(curr_branch);
 +      am_destroy(state);
 +}
 +
 +/**
 + * parse_options() callback that validates and sets opt->value to the
 + * PATCH_FORMAT_* enum value corresponding to `arg`.
 + */
 +static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset)
 +{
 +      int *opt_value = opt->value;
 +
 +      if (!strcmp(arg, "mbox"))
 +              *opt_value = PATCH_FORMAT_MBOX;
 +      else if (!strcmp(arg, "stgit"))
 +              *opt_value = PATCH_FORMAT_STGIT;
 +      else if (!strcmp(arg, "stgit-series"))
 +              *opt_value = PATCH_FORMAT_STGIT_SERIES;
 +      else if (!strcmp(arg, "hg"))
 +              *opt_value = PATCH_FORMAT_HG;
 +      else
 +              return error(_("Invalid value for --patch-format: %s"), arg);
 +      return 0;
 +}
 +
 +enum resume_mode {
 +      RESUME_FALSE = 0,
 +      RESUME_APPLY,
 +      RESUME_RESOLVED,
 +      RESUME_SKIP,
 +      RESUME_ABORT
 +};
 +
 +int cmd_am(int argc, const char **argv, const char *prefix)
 +{
 +      struct am_state state;
 +      int binary = -1;
 +      int keep_cr = -1;
 +      int patch_format = PATCH_FORMAT_UNKNOWN;
 +      enum resume_mode resume = RESUME_FALSE;
 +      int in_progress;
 +
 +      const char * const usage[] = {
 +              N_("git am [options] [(<mbox>|<Maildir>)...]"),
 +              N_("git am [options] (--continue | --skip | --abort)"),
 +              NULL
 +      };
 +
 +      struct option options[] = {
 +              OPT_BOOL('i', "interactive", &state.interactive,
 +                      N_("run interactively")),
 +              OPT_HIDDEN_BOOL('b', "binary", &binary,
 +                      N_("(historical option -- no-op")),
 +              OPT_BOOL('3', "3way", &state.threeway,
 +                      N_("allow fall back on 3way merging if needed")),
 +              OPT__QUIET(&state.quiet, N_("be quiet")),
 +              OPT_SET_INT('s', "signoff", &state.signoff,
 +                      N_("add a Signed-off-by line to the commit message"),
 +                      SIGNOFF_EXPLICIT),
 +              OPT_BOOL('u', "utf8", &state.utf8,
 +                      N_("recode into utf8 (default)")),
 +              OPT_SET_INT('k', "keep", &state.keep,
 +                      N_("pass -k flag to git-mailinfo"), KEEP_TRUE),
 +              OPT_SET_INT(0, "keep-non-patch", &state.keep,
 +                      N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),
 +              OPT_BOOL('m', "message-id", &state.message_id,
 +                      N_("pass -m flag to git-mailinfo")),
 +              { OPTION_SET_INT, 0, "keep-cr", &keep_cr, NULL,
 +                N_("pass --keep-cr flag to git-mailsplit for mbox format"),
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
 +              { OPTION_SET_INT, 0, "no-keep-cr", &keep_cr, NULL,
 +                N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
 +              OPT_BOOL('c', "scissors", &state.scissors,
 +                      N_("strip everything before a scissors line")),
 +              OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL,
 +                      N_("pass it through git-apply"),
 +                      PARSE_OPT_NOARG),
 +              OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL,
 +                      N_("pass it through git-apply"),
 +                      PARSE_OPT_NOARG),
 +              OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"),
 +                      N_("pass it through git-apply"),
 +                      0),
 +              OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"),
 +                      N_("format the patch(es) are in"),
 +                      parse_opt_patchformat),
 +              OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL,
 +                      N_("pass it through git-apply"),
 +                      PARSE_OPT_NOARG),
 +              OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
 +                      N_("override error message when patch failure occurs")),
 +              OPT_CMDMODE(0, "continue", &resume,
 +                      N_("continue applying patches after resolving a conflict"),
 +                      RESUME_RESOLVED),
 +              OPT_CMDMODE('r', "resolved", &resume,
 +                      N_("synonyms for --continue"),
 +                      RESUME_RESOLVED),
 +              OPT_CMDMODE(0, "skip", &resume,
 +                      N_("skip the current patch"),
 +                      RESUME_SKIP),
 +              OPT_CMDMODE(0, "abort", &resume,
 +                      N_("restore the original branch and abort the patching operation."),
 +                      RESUME_ABORT),
 +              OPT_BOOL(0, "committer-date-is-author-date",
 +                      &state.committer_date_is_author_date,
 +                      N_("lie about committer date")),
 +              OPT_BOOL(0, "ignore-date", &state.ignore_date,
 +                      N_("use current timestamp for author date")),
 +              OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),
 +              { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 +                N_("GPG-sign commits"),
 +                PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 +              OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 +                      N_("(internal use for git-rebase)")),
 +              OPT_END()
 +      };
 +
 +      git_config(git_default_config, NULL);
 +
 +      am_state_init(&state, git_path("rebase-apply"));
 +
 +      in_progress = am_in_progress(&state);
 +      if (in_progress)
 +              am_load(&state);
 +
 +      argc = parse_options(argc, argv, prefix, options, usage, 0);
 +
 +      if (binary >= 0)
 +              fprintf_ln(stderr, _("The -b/--binary option has been a no-op for long time, and\n"
 +                              "it will be removed. Please do not use it anymore."));
 +
 +      /* Ensure a valid committer ident can be constructed */
 +      git_committer_info(IDENT_STRICT);
 +
 +      if (read_index_preload(&the_index, NULL) < 0)
 +              die(_("failed to read the index"));
 +
 +      if (in_progress) {
 +              /*
 +               * Catch user error to feed us patches when there is a session
 +               * in progress:
 +               *
 +               * 1. mbox path(s) are provided on the command-line.
 +               * 2. stdin is not a tty: the user is trying to feed us a patch
 +               *    from standard input. This is somewhat unreliable -- stdin
 +               *    could be /dev/null for example and the caller did not
 +               *    intend to feed us a patch but wanted to continue
 +               *    unattended.
 +               */
 +              if (argc || (resume == RESUME_FALSE && !isatty(0)))
 +                      die(_("previous rebase directory %s still exists but mbox given."),
 +                              state.dir);
 +
 +              if (resume == RESUME_FALSE)
 +                      resume = RESUME_APPLY;
 +
 +              if (state.signoff == SIGNOFF_EXPLICIT)
 +                      am_append_signoff(&state);
 +      } else {
 +              struct argv_array paths = ARGV_ARRAY_INIT;
 +              int i;
 +
 +              /*
 +               * Handle stray state directory in the independent-run case. In
 +               * the --rebasing case, it is up to the caller to take care of
 +               * stray directories.
 +               */
 +              if (file_exists(state.dir) && !state.rebasing) {
 +                      if (resume == RESUME_ABORT) {
 +                              am_destroy(&state);
 +                              am_state_release(&state);
 +                              return 0;
 +                      }
 +
 +                      die(_("Stray %s directory found.\n"
 +                              "Use \"git am --abort\" to remove it."),
 +                              state.dir);
 +              }
 +
 +              if (resume)
 +                      die(_("Resolve operation not in progress, we are not resuming."));
 +
 +              for (i = 0; i < argc; i++) {
 +                      if (is_absolute_path(argv[i]) || !prefix)
 +                              argv_array_push(&paths, argv[i]);
 +                      else
 +                              argv_array_push(&paths, mkpath("%s/%s", prefix, argv[i]));
 +              }
 +
 +              am_setup(&state, patch_format, paths.argv, keep_cr);
 +
 +              argv_array_clear(&paths);
 +      }
 +
 +      switch (resume) {
 +      case RESUME_FALSE:
 +              am_run(&state, 0);
 +              break;
 +      case RESUME_APPLY:
 +              am_run(&state, 1);
 +              break;
 +      case RESUME_RESOLVED:
 +              am_resolve(&state);
 +              break;
 +      case RESUME_SKIP:
 +              am_skip(&state);
 +              break;
 +      case RESUME_ABORT:
 +              am_abort(&state);
 +              break;
 +      default:
 +              die("BUG: invalid resume value");
 +      }
 +
 +      am_state_release(&state);
 +
 +      return 0;
 +}
diff --combined builtin/commit.c
index 4cbd5ff4de97aa41932bfe3e0826d71a54fe4f43,96aee0c0cc8f6a0a594909ba0c9feae08bd15539..b37cb6c8b7966161ed156d15d9a910824b645dda
@@@ -166,9 -166,9 +166,9 @@@ static int opt_parse_m(const struct opt
  
  static void determine_whence(struct wt_status *s)
  {
 -      if (file_exists(git_path("MERGE_HEAD")))
 +      if (file_exists(git_path_merge_head()))
                whence = FROM_MERGE;
 -      else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
 +      else if (file_exists(git_path_cherry_pick_head())) {
                whence = FROM_CHERRY_PICK;
                if (file_exists(git_path(SEQ_DIR)))
                        sequencer_in_use = 1;
@@@ -324,6 -324,7 +324,7 @@@ static const char *prepare_index(int ar
        struct string_list partial;
        struct pathspec pathspec;
        int refresh_flags = REFRESH_QUIET;
+       const char *ret;
  
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
                        die(_("unable to create temporary index"));
  
                old_index_env = getenv(INDEX_ENVIRONMENT);
-               setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
+               setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 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.buf);
+               read_cache_from(get_lock_file_path(&index_lock));
                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.buf;
+               return get_lock_file_path(&index_lock);
        }
  
        /*
                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.buf;
+               return get_lock_file_path(&index_lock);
        }
  
        /*
                die(_("unable to write temporary index file"));
  
        discard_cache();
-       read_cache_from(false_lock.filename.buf);
-       return false_lock.filename.buf;
+       ret = get_lock_file_path(&false_lock);
+       read_cache_from(ret);
+       return ret;
  }
  
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
@@@ -725,12 -726,12 +726,12 @@@ static int prepare_to_commit(const cha
                format_commit_message(commit, "fixup! %s\n\n",
                                      &sb, &ctx);
                hook_arg1 = "message";
 -      } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
 -              if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
 +      } else if (!stat(git_path_merge_msg(), &statbuf)) {
 +              if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
                hook_arg1 = "merge";
 -      } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
 -              if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
 +      } else if (!stat(git_path_squash_msg(), &statbuf)) {
 +              if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
                hook_arg1 = "squash";
        } else if (template_file) {
                                _("%s"
                                "Date:      %s"),
                                ident_shown++ ? "" : "\n",
 -                              show_ident_date(&ai, DATE_NORMAL));
 +                              show_ident_date(&ai, DATE_MODE(NORMAL)));
  
                if (!committer_ident_sufficiently_given())
                        status_printf_ln(s, GIT_COLOR_NORMAL,
@@@ -1046,7 -1047,7 +1047,7 @@@ static const char *find_author_by_nickn
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
 -              ctx.date_mode = DATE_NORMAL;
 +              ctx.date_mode.type = DATE_NORMAL;
                strbuf_release(&buf);
                format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
                clear_mailmap(&mailmap);
@@@ -1684,10 -1685,10 +1685,10 @@@ int cmd_commit(int argc, const char **a
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
                pptr = &commit_list_insert(current_head, pptr)->next;
 -              fp = fopen(git_path("MERGE_HEAD"), "r");
 +              fp = fopen(git_path_merge_head(), "r");
                if (fp == NULL)
                        die_errno(_("could not open '%s' for reading"),
 -                                git_path("MERGE_HEAD"));
 +                                git_path_merge_head());
                while (strbuf_getline(&m, fp, '\n') != EOF) {
                        struct commit *parent;
  
                }
                fclose(fp);
                strbuf_release(&m);
 -              if (!stat(git_path("MERGE_MODE"), &statbuf)) {
 -                      if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
 +              if (!stat(git_path_merge_mode(), &statbuf)) {
 +                      if (strbuf_read_file(&sb, git_path_merge_mode(), 0) < 0)
                                die_errno(_("could not read MERGE_MODE"));
                        if (!strcmp(sb.buf, "no-ff"))
                                allow_fast_forward = 0;
        }
        ref_transaction_free(transaction);
  
 -      unlink(git_path("CHERRY_PICK_HEAD"));
 -      unlink(git_path("REVERT_HEAD"));
 -      unlink(git_path("MERGE_HEAD"));
 -      unlink(git_path("MERGE_MSG"));
 -      unlink(git_path("MERGE_MODE"));
 -      unlink(git_path("SQUASH_MSG"));
 +      unlink(git_path_cherry_pick_head());
 +      unlink(git_path_revert_head());
 +      unlink(git_path_merge_head());
 +      unlink(git_path_merge_msg());
 +      unlink(git_path_merge_mode());
 +      unlink(git_path_squash_msg());
  
        if (commit_index_files())
                die (_("Repository has been updated, but unable to write\n"
diff --combined builtin/gc.c
index bcc75d9bfc4e256dc222d65a3e43a6c090f08b1a,bfe589f4978aa7dc300fb97fa773df7b5c168ed4..0ad8d30b56f89a9ea6ac3a9ee5ae4593ae9754a0
@@@ -11,6 -11,7 +11,7 @@@
   */
  
  #include "builtin.h"
+ #include "tempfile.h"
  #include "lockfile.h"
  #include "parse-options.h"
  #include "run-command.h"
@@@ -42,20 -43,7 +43,7 @@@ static struct argv_array prune = ARGV_A
  static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
  static struct argv_array rerere = ARGV_ARRAY_INIT;
  
- static char *pidfile;
- static void remove_pidfile(void)
- {
-       if (pidfile)
-               unlink(pidfile);
- }
- static void remove_pidfile_on_signal(int signo)
- {
-       remove_pidfile();
-       sigchain_pop(signo);
-       raise(signo);
- }
+ static struct tempfile pidfile;
  
  static void git_config_date_string(const char *key, const char **output)
  {
@@@ -85,7 -73,7 +73,7 @@@ static void gc_config(void
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_date_string("gc.pruneexpire", &prune_expire);
 -      git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire);
 +      git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
        git_config(git_default_config, NULL);
  }
  
@@@ -199,20 -187,22 +187,22 @@@ static const char *lock_repo_for_gc(in
        uintmax_t pid;
        FILE *fp;
        int fd;
+       char *pidfile_path;
  
-       if (pidfile)
+       if (is_tempfile_active(&pidfile))
                /* already locked */
                return NULL;
  
        if (gethostname(my_host, sizeof(my_host)))
                strcpy(my_host, "unknown");
  
-       fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
+       pidfile_path = git_pathdup("gc.pid");
+       fd = hold_lock_file_for_update(&lock, pidfile_path,
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
                static char locking_host[128];
                int should_exit;
-               fp = fopen(git_path("gc.pid"), "r");
+               fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
                        fp != NULL &&
                        if (fd >= 0)
                                rollback_lock_file(&lock);
                        *ret_pid = pid;
+                       free(pidfile_path);
                        return locking_host;
                }
        }
        write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
        commit_lock_file(&lock);
-       pidfile = git_pathdup("gc.pid");
-       sigchain_push_common(remove_pidfile_on_signal);
-       atexit(remove_pidfile);
+       register_tempfile(&pidfile, pidfile_path);
+       free(pidfile_path);
        return NULL;
  }
  
@@@ -293,7 -281,7 +281,7 @@@ int cmd_gc(int argc, const char **argv
        argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
        argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
        argv_array_pushl(&prune, "prune", "--expire", NULL);
 -      argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL);
 +      argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
        argv_array_pushl(&rerere, "rerere", "gc", NULL);
  
        gc_config();
diff --combined builtin/pull.c
index b7bc1fff5e0c05782dbb9e9bf3210248db15c54a,0000000000000000000000000000000000000000..7e3c11ea6351c9703fe7d50057392f0548e03160
mode 100644,000000..100644
--- /dev/null
@@@ -1,886 -1,0 +1,887 @@@
 +/*
 + * Builtin "git pull"
 + *
 + * Based on git-pull.sh by Junio C Hamano
 + *
 + * Fetch one or more remote refs and merge it/them into the current HEAD.
 + */
 +#include "cache.h"
 +#include "builtin.h"
 +#include "parse-options.h"
 +#include "exec_cmd.h"
 +#include "run-command.h"
 +#include "sha1-array.h"
 +#include "remote.h"
 +#include "dir.h"
 +#include "refs.h"
 +#include "revision.h"
++#include "tempfile.h"
 +#include "lockfile.h"
 +
 +enum rebase_type {
 +      REBASE_INVALID = -1,
 +      REBASE_FALSE = 0,
 +      REBASE_TRUE,
 +      REBASE_PRESERVE
 +};
 +
 +/**
 + * Parses the value of --rebase. If value is a false value, returns
 + * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
 + * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with
 + * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
 + */
 +static enum rebase_type parse_config_rebase(const char *key, const char *value,
 +              int fatal)
 +{
 +      int v = git_config_maybe_bool("pull.rebase", value);
 +
 +      if (!v)
 +              return REBASE_FALSE;
 +      else if (v > 0)
 +              return REBASE_TRUE;
 +      else if (!strcmp(value, "preserve"))
 +              return REBASE_PRESERVE;
 +
 +      if (fatal)
 +              die(_("Invalid value for %s: %s"), key, value);
 +      else
 +              error(_("Invalid value for %s: %s"), key, value);
 +
 +      return REBASE_INVALID;
 +}
 +
 +/**
 + * Callback for --rebase, which parses arg with parse_config_rebase().
 + */
 +static int parse_opt_rebase(const struct option *opt, const char *arg, int unset)
 +{
 +      enum rebase_type *value = opt->value;
 +
 +      if (arg)
 +              *value = parse_config_rebase("--rebase", arg, 0);
 +      else
 +              *value = unset ? REBASE_FALSE : REBASE_TRUE;
 +      return *value == REBASE_INVALID ? -1 : 0;
 +}
 +
 +static const char * const pull_usage[] = {
 +      N_("git pull [options] [<repository> [<refspec>...]]"),
 +      NULL
 +};
 +
 +/* Shared options */
 +static int opt_verbosity;
 +static char *opt_progress;
 +
 +/* Options passed to git-merge or git-rebase */
 +static enum rebase_type opt_rebase = -1;
 +static char *opt_diffstat;
 +static char *opt_log;
 +static char *opt_squash;
 +static char *opt_commit;
 +static char *opt_edit;
 +static char *opt_ff;
 +static char *opt_verify_signatures;
 +static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
 +static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
 +static char *opt_gpg_sign;
 +
 +/* Options passed to git-fetch */
 +static char *opt_all;
 +static char *opt_append;
 +static char *opt_upload_pack;
 +static int opt_force;
 +static char *opt_tags;
 +static char *opt_prune;
 +static char *opt_recurse_submodules;
 +static int opt_dry_run;
 +static char *opt_keep;
 +static char *opt_depth;
 +static char *opt_unshallow;
 +static char *opt_update_shallow;
 +static char *opt_refmap;
 +
 +static struct option pull_options[] = {
 +      /* Shared options */
 +      OPT__VERBOSITY(&opt_verbosity),
 +      OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
 +              N_("force progress reporting"),
 +              PARSE_OPT_NOARG),
 +
 +      /* Options passed to git-merge or git-rebase */
 +      OPT_GROUP(N_("Options related to merging")),
 +      { OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
 +        N_("false|true|preserve"),
 +        N_("incorporate changes by rebasing rather than merging"),
 +        PARSE_OPT_OPTARG, parse_opt_rebase },
 +      OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
 +              N_("do not show a diffstat at the end of the merge"),
 +              PARSE_OPT_NOARG | PARSE_OPT_NONEG),
 +      OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
 +              N_("show a diffstat at the end of the merge"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
 +              N_("(synonym to --stat)"),
 +              PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
 +      OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
 +              N_("add (at most <n>) entries from shortlog to merge commit message"),
 +              PARSE_OPT_OPTARG),
 +      OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
 +              N_("create a single commit instead of doing a merge"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
 +              N_("perform a commit if the merge succeeds (default)"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
 +              N_("edit message before committing"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
 +              N_("allow fast-forward"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
 +              N_("abort if fast-forward is not possible"),
 +              PARSE_OPT_NOARG | PARSE_OPT_NONEG),
 +      OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
 +              N_("verify that the named commit has a valid GPG signature"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 +              N_("merge strategy to use"),
 +              0),
 +      OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
 +              N_("option=value"),
 +              N_("option for selected merge strategy"),
 +              0),
 +      OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
 +              N_("GPG sign commit"),
 +              PARSE_OPT_OPTARG),
 +
 +      /* Options passed to git-fetch */
 +      OPT_GROUP(N_("Options related to fetching")),
 +      OPT_PASSTHRU(0, "all", &opt_all, NULL,
 +              N_("fetch from all remotes"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU('a', "append", &opt_append, NULL,
 +              N_("append to .git/FETCH_HEAD instead of overwriting"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
 +              N_("path to upload pack on remote end"),
 +              0),
 +      OPT__FORCE(&opt_force, N_("force overwrite of local branch")),
 +      OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
 +              N_("fetch all tags and associated objects"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
 +              N_("prune remote-tracking branches no longer on remote"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
 +              N_("on-demand"),
 +              N_("control recursive fetching of submodules"),
 +              PARSE_OPT_OPTARG),
 +      OPT_BOOL(0, "dry-run", &opt_dry_run,
 +              N_("dry run")),
 +      OPT_PASSTHRU('k', "keep", &opt_keep, NULL,
 +              N_("keep downloaded pack"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
 +              N_("deepen history of shallow clone"),
 +              0),
 +      OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
 +              N_("convert to a complete repository"),
 +              PARSE_OPT_NONEG | PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL,
 +              N_("accept refs that update .git/shallow"),
 +              PARSE_OPT_NOARG),
 +      OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
 +              N_("specify fetch refmap"),
 +              PARSE_OPT_NONEG),
 +
 +      OPT_END()
 +};
 +
 +/**
 + * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
 + */
 +static void argv_push_verbosity(struct argv_array *arr)
 +{
 +      int verbosity;
 +
 +      for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
 +              argv_array_push(arr, "-v");
 +
 +      for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
 +              argv_array_push(arr, "-q");
 +}
 +
 +/**
 + * Pushes "-f" switches into arr to match the opt_force level.
 + */
 +static void argv_push_force(struct argv_array *arr)
 +{
 +      int force = opt_force;
 +      while (force-- > 0)
 +              argv_array_push(arr, "-f");
 +}
 +
 +/**
 + * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv
 + */
 +static void set_reflog_message(int argc, const char **argv)
 +{
 +      int i;
 +      struct strbuf msg = STRBUF_INIT;
 +
 +      for (i = 0; i < argc; i++) {
 +              if (i)
 +                      strbuf_addch(&msg, ' ');
 +              strbuf_addstr(&msg, argv[i]);
 +      }
 +
 +      setenv("GIT_REFLOG_ACTION", msg.buf, 0);
 +
 +      strbuf_release(&msg);
 +}
 +
 +/**
 + * If pull.ff is unset, returns NULL. If pull.ff is "true", returns "--ff". If
 + * pull.ff is "false", returns "--no-ff". If pull.ff is "only", returns
 + * "--ff-only". Otherwise, if pull.ff is set to an invalid value, die with an
 + * error.
 + */
 +static const char *config_get_ff(void)
 +{
 +      const char *value;
 +
 +      if (git_config_get_value("pull.ff", &value))
 +              return NULL;
 +
 +      switch (git_config_maybe_bool("pull.ff", value)) {
 +      case 0:
 +              return "--no-ff";
 +      case 1:
 +              return "--ff";
 +      }
 +
 +      if (!strcmp(value, "only"))
 +              return "--ff-only";
 +
 +      die(_("Invalid value for pull.ff: %s"), value);
 +}
 +
 +/**
 + * Returns the default configured value for --rebase. It first looks for the
 + * value of "branch.$curr_branch.rebase", where $curr_branch is the current
 + * branch, and if HEAD is detached or the configuration key does not exist,
 + * looks for the value of "pull.rebase". If both configuration keys do not
 + * exist, returns REBASE_FALSE.
 + */
 +static enum rebase_type config_get_rebase(void)
 +{
 +      struct branch *curr_branch = branch_get("HEAD");
 +      const char *value;
 +
 +      if (curr_branch) {
 +              char *key = xstrfmt("branch.%s.rebase", curr_branch->name);
 +
 +              if (!git_config_get_value(key, &value)) {
 +                      enum rebase_type ret = parse_config_rebase(key, value, 1);
 +                      free(key);
 +                      return ret;
 +              }
 +
 +              free(key);
 +      }
 +
 +      if (!git_config_get_value("pull.rebase", &value))
 +              return parse_config_rebase("pull.rebase", value, 1);
 +
 +      return REBASE_FALSE;
 +}
 +
 +/**
 + * Returns 1 if there are unstaged changes, 0 otherwise.
 + */
 +static int has_unstaged_changes(const char *prefix)
 +{
 +      struct rev_info rev_info;
 +      int result;
 +
 +      init_revisions(&rev_info, prefix);
 +      DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
 +      DIFF_OPT_SET(&rev_info.diffopt, QUICK);
 +      diff_setup_done(&rev_info.diffopt);
 +      result = run_diff_files(&rev_info, 0);
 +      return diff_result_code(&rev_info.diffopt, result);
 +}
 +
 +/**
 + * Returns 1 if there are uncommitted changes, 0 otherwise.
 + */
 +static int has_uncommitted_changes(const char *prefix)
 +{
 +      struct rev_info rev_info;
 +      int result;
 +
 +      if (is_cache_unborn())
 +              return 0;
 +
 +      init_revisions(&rev_info, prefix);
 +      DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
 +      DIFF_OPT_SET(&rev_info.diffopt, QUICK);
 +      add_head_to_pending(&rev_info);
 +      diff_setup_done(&rev_info.diffopt);
 +      result = run_diff_index(&rev_info, 1);
 +      return diff_result_code(&rev_info.diffopt, result);
 +}
 +
 +/**
 + * If the work tree has unstaged or uncommitted changes, dies with the
 + * appropriate message.
 + */
 +static void die_on_unclean_work_tree(const char *prefix)
 +{
 +      struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
 +      int do_die = 0;
 +
 +      hold_locked_index(lock_file, 0);
 +      refresh_cache(REFRESH_QUIET);
 +      update_index_if_able(&the_index, lock_file);
 +      rollback_lock_file(lock_file);
 +
 +      if (has_unstaged_changes(prefix)) {
 +              error(_("Cannot pull with rebase: You have unstaged changes."));
 +              do_die = 1;
 +      }
 +
 +      if (has_uncommitted_changes(prefix)) {
 +              if (do_die)
 +                      error(_("Additionally, your index contains uncommitted changes."));
 +              else
 +                      error(_("Cannot pull with rebase: Your index contains uncommitted changes."));
 +              do_die = 1;
 +      }
 +
 +      if (do_die)
 +              exit(1);
 +}
 +
 +/**
 + * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
 + * into merge_heads.
 + */
 +static void get_merge_heads(struct sha1_array *merge_heads)
 +{
 +      const char *filename = git_path("FETCH_HEAD");
 +      FILE *fp;
 +      struct strbuf sb = STRBUF_INIT;
 +      unsigned char sha1[GIT_SHA1_RAWSZ];
 +
 +      if (!(fp = fopen(filename, "r")))
 +              die_errno(_("could not open '%s' for reading"), filename);
 +      while (strbuf_getline(&sb, fp, '\n') != EOF) {
 +              if (get_sha1_hex(sb.buf, sha1))
 +                      continue;  /* invalid line: does not start with SHA1 */
 +              if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
 +                      continue;  /* ref is not-for-merge */
 +              sha1_array_append(merge_heads, sha1);
 +      }
 +      fclose(fp);
 +      strbuf_release(&sb);
 +}
 +
 +/**
 + * Used by die_no_merge_candidates() as a for_each_remote() callback to
 + * retrieve the name of the remote if the repository only has one remote.
 + */
 +static int get_only_remote(struct remote *remote, void *cb_data)
 +{
 +      const char **remote_name = cb_data;
 +
 +      if (*remote_name)
 +              return -1;
 +
 +      *remote_name = remote->name;
 +      return 0;
 +}
 +
 +/**
 + * Dies with the appropriate reason for why there are no merge candidates:
 + *
 + * 1. We fetched from a specific remote, and a refspec was given, but it ended
 + *    up not fetching anything. This is usually because the user provided a
 + *    wildcard refspec which had no matches on the remote end.
 + *
 + * 2. We fetched from a non-default remote, but didn't specify a branch to
 + *    merge. We can't use the configured one because it applies to the default
 + *    remote, thus the user must specify the branches to merge.
 + *
 + * 3. We fetched from the branch's or repo's default remote, but:
 + *
 + *    a. We are not on a branch, so there will never be a configured branch to
 + *       merge with.
 + *
 + *    b. We are on a branch, but there is no configured branch to merge with.
 + *
 + * 4. We fetched from the branch's or repo's default remote, but the configured
 + *    branch to merge didn't get fetched. (Either it doesn't exist, or wasn't
 + *    part of the configured fetch refspec.)
 + */
 +static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs)
 +{
 +      struct branch *curr_branch = branch_get("HEAD");
 +      const char *remote = curr_branch ? curr_branch->remote_name : NULL;
 +
 +      if (*refspecs) {
 +              if (opt_rebase)
 +                      fprintf_ln(stderr, _("There is no candidate for rebasing against among the refs that you just fetched."));
 +              else
 +                      fprintf_ln(stderr, _("There are no candidates for merging among the refs that you just fetched."));
 +              fprintf_ln(stderr, _("Generally this means that you provided a wildcard refspec which had no\n"
 +                                      "matches on the remote end."));
 +      } else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {
 +              fprintf_ln(stderr, _("You asked to pull from the remote '%s', but did not specify\n"
 +                      "a branch. Because this is not the default configured remote\n"
 +                      "for your current branch, you must specify a branch on the command line."),
 +                      repo);
 +      } else if (!curr_branch) {
 +              fprintf_ln(stderr, _("You are not currently on a branch."));
 +              if (opt_rebase)
 +                      fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
 +              else
 +                      fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
 +              fprintf_ln(stderr, _("See git-pull(1) for details."));
 +              fprintf(stderr, "\n");
 +              fprintf_ln(stderr, "    git pull <remote> <branch>");
 +              fprintf(stderr, "\n");
 +      } else if (!curr_branch->merge_nr) {
 +              const char *remote_name = NULL;
 +
 +              if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
 +                      remote_name = "<remote>";
 +
 +              fprintf_ln(stderr, _("There is no tracking information for the current branch."));
 +              if (opt_rebase)
 +                      fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
 +              else
 +                      fprintf_ln(stderr, _("Please specify which branch you want to merge with."));
 +              fprintf_ln(stderr, _("See git-pull(1) for details."));
 +              fprintf(stderr, "\n");
 +              fprintf_ln(stderr, "    git pull <remote> <branch>");
 +              fprintf(stderr, "\n");
 +              fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:\n"
 +                              "\n"
 +                              "    git branch --set-upstream-to=%s/<branch> %s\n"),
 +                              remote_name, curr_branch->name);
 +      } else
 +              fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n"
 +                      "from the remote, but no such ref was fetched."),
 +                      *curr_branch->merge_name);
 +      exit(1);
 +}
 +
 +/**
 + * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`
 + * as a string and `refspecs` as a null-terminated array of strings. If `repo`
 + * is not provided in argv, it is set to NULL.
 + */
 +static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
 +              const char ***refspecs)
 +{
 +      if (argc > 0) {
 +              *repo = *argv++;
 +              argc--;
 +      } else
 +              *repo = NULL;
 +      *refspecs = argv;
 +}
 +
 +/**
 + * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the
 + * repository and refspecs to fetch, or NULL if they are not provided.
 + */
 +static int run_fetch(const char *repo, const char **refspecs)
 +{
 +      struct argv_array args = ARGV_ARRAY_INIT;
 +      int ret;
 +
 +      argv_array_pushl(&args, "fetch", "--update-head-ok", NULL);
 +
 +      /* Shared options */
 +      argv_push_verbosity(&args);
 +      if (opt_progress)
 +              argv_array_push(&args, opt_progress);
 +
 +      /* Options passed to git-fetch */
 +      if (opt_all)
 +              argv_array_push(&args, opt_all);
 +      if (opt_append)
 +              argv_array_push(&args, opt_append);
 +      if (opt_upload_pack)
 +              argv_array_push(&args, opt_upload_pack);
 +      argv_push_force(&args);
 +      if (opt_tags)
 +              argv_array_push(&args, opt_tags);
 +      if (opt_prune)
 +              argv_array_push(&args, opt_prune);
 +      if (opt_recurse_submodules)
 +              argv_array_push(&args, opt_recurse_submodules);
 +      if (opt_dry_run)
 +              argv_array_push(&args, "--dry-run");
 +      if (opt_keep)
 +              argv_array_push(&args, opt_keep);
 +      if (opt_depth)
 +              argv_array_push(&args, opt_depth);
 +      if (opt_unshallow)
 +              argv_array_push(&args, opt_unshallow);
 +      if (opt_update_shallow)
 +              argv_array_push(&args, opt_update_shallow);
 +      if (opt_refmap)
 +              argv_array_push(&args, opt_refmap);
 +
 +      if (repo) {
 +              argv_array_push(&args, repo);
 +              argv_array_pushv(&args, refspecs);
 +      } else if (*refspecs)
 +              die("BUG: refspecs without repo?");
 +      ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 +      argv_array_clear(&args);
 +      return ret;
 +}
 +
 +/**
 + * "Pulls into void" by branching off merge_head.
 + */
 +static int pull_into_void(const unsigned char *merge_head,
 +              const unsigned char *curr_head)
 +{
 +      /*
 +       * Two-way merge: we treat the index as based on an empty tree,
 +       * and try to fast-forward to HEAD. This ensures we will not lose
 +       * index/worktree changes that the user already made on the unborn
 +       * branch.
 +       */
 +      if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))
 +              return 1;
 +
 +      if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
 +              return 1;
 +
 +      return 0;
 +}
 +
 +/**
 + * Runs git-merge, returning its exit status.
 + */
 +static int run_merge(void)
 +{
 +      int ret;
 +      struct argv_array args = ARGV_ARRAY_INIT;
 +
 +      argv_array_pushl(&args, "merge", NULL);
 +
 +      /* Shared options */
 +      argv_push_verbosity(&args);
 +      if (opt_progress)
 +              argv_array_push(&args, opt_progress);
 +
 +      /* Options passed to git-merge */
 +      if (opt_diffstat)
 +              argv_array_push(&args, opt_diffstat);
 +      if (opt_log)
 +              argv_array_push(&args, opt_log);
 +      if (opt_squash)
 +              argv_array_push(&args, opt_squash);
 +      if (opt_commit)
 +              argv_array_push(&args, opt_commit);
 +      if (opt_edit)
 +              argv_array_push(&args, opt_edit);
 +      if (opt_ff)
 +              argv_array_push(&args, opt_ff);
 +      if (opt_verify_signatures)
 +              argv_array_push(&args, opt_verify_signatures);
 +      argv_array_pushv(&args, opt_strategies.argv);
 +      argv_array_pushv(&args, opt_strategy_opts.argv);
 +      if (opt_gpg_sign)
 +              argv_array_push(&args, opt_gpg_sign);
 +
 +      argv_array_push(&args, "FETCH_HEAD");
 +      ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 +      argv_array_clear(&args);
 +      return ret;
 +}
 +
 +/**
 + * Returns remote's upstream branch for the current branch. If remote is NULL,
 + * the current branch's configured default remote is used. Returns NULL if
 + * `remote` does not name a valid remote, HEAD does not point to a branch,
 + * remote is not the branch's configured remote or the branch does not have any
 + * configured upstream branch.
 + */
 +static const char *get_upstream_branch(const char *remote)
 +{
 +      struct remote *rm;
 +      struct branch *curr_branch;
 +      const char *curr_branch_remote;
 +
 +      rm = remote_get(remote);
 +      if (!rm)
 +              return NULL;
 +
 +      curr_branch = branch_get("HEAD");
 +      if (!curr_branch)
 +              return NULL;
 +
 +      curr_branch_remote = remote_for_branch(curr_branch, NULL);
 +      assert(curr_branch_remote);
 +
 +      if (strcmp(curr_branch_remote, rm->name))
 +              return NULL;
 +
 +      return branch_get_upstream(curr_branch, NULL);
 +}
 +
 +/**
 + * Derives the remote tracking branch from the remote and refspec.
 + *
 + * FIXME: The current implementation assumes the default mapping of
 + * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>.
 + */
 +static const char *get_tracking_branch(const char *remote, const char *refspec)
 +{
 +      struct refspec *spec;
 +      const char *spec_src;
 +      const char *merge_branch;
 +
 +      spec = parse_fetch_refspec(1, &refspec);
 +      spec_src = spec->src;
 +      if (!*spec_src || !strcmp(spec_src, "HEAD"))
 +              spec_src = "HEAD";
 +      else if (skip_prefix(spec_src, "heads/", &spec_src))
 +              ;
 +      else if (skip_prefix(spec_src, "refs/heads/", &spec_src))
 +              ;
 +      else if (starts_with(spec_src, "refs/") ||
 +              starts_with(spec_src, "tags/") ||
 +              starts_with(spec_src, "remotes/"))
 +              spec_src = "";
 +
 +      if (*spec_src) {
 +              if (!strcmp(remote, "."))
 +                      merge_branch = mkpath("refs/heads/%s", spec_src);
 +              else
 +                      merge_branch = mkpath("refs/remotes/%s/%s", remote, spec_src);
 +      } else
 +              merge_branch = NULL;
 +
 +      free_refspec(1, spec);
 +      return merge_branch;
 +}
 +
 +/**
 + * Given the repo and refspecs, sets fork_point to the point at which the
 + * current branch forked from its remote tracking branch. Returns 0 on success,
 + * -1 on failure.
 + */
 +static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
 +              const char *refspec)
 +{
 +      int ret;
 +      struct branch *curr_branch;
 +      const char *remote_branch;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      curr_branch = branch_get("HEAD");
 +      if (!curr_branch)
 +              return -1;
 +
 +      if (refspec)
 +              remote_branch = get_tracking_branch(repo, refspec);
 +      else
 +              remote_branch = get_upstream_branch(repo);
 +
 +      if (!remote_branch)
 +              return -1;
 +
 +      argv_array_pushl(&cp.args, "merge-base", "--fork-point",
 +                      remote_branch, curr_branch->name, NULL);
 +      cp.no_stdin = 1;
 +      cp.no_stderr = 1;
 +      cp.git_cmd = 1;
 +
 +      ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ);
 +      if (ret)
 +              goto cleanup;
 +
 +      ret = get_sha1_hex(sb.buf, fork_point);
 +      if (ret)
 +              goto cleanup;
 +
 +cleanup:
 +      strbuf_release(&sb);
 +      return ret ? -1 : 0;
 +}
 +
 +/**
 + * Sets merge_base to the octopus merge base of curr_head, merge_head and
 + * fork_point. Returns 0 if a merge base is found, 1 otherwise.
 + */
 +static int get_octopus_merge_base(unsigned char *merge_base,
 +              const unsigned char *curr_head,
 +              const unsigned char *merge_head,
 +              const unsigned char *fork_point)
 +{
 +      struct commit_list *revs = NULL, *result;
 +
 +      commit_list_insert(lookup_commit_reference(curr_head), &revs);
 +      commit_list_insert(lookup_commit_reference(merge_head), &revs);
 +      if (!is_null_sha1(fork_point))
 +              commit_list_insert(lookup_commit_reference(fork_point), &revs);
 +
 +      result = reduce_heads(get_octopus_merge_bases(revs));
 +      free_commit_list(revs);
 +      if (!result)
 +              return 1;
 +
 +      hashcpy(merge_base, result->item->object.sha1);
 +      return 0;
 +}
 +
 +/**
 + * Given the current HEAD SHA1, the merge head returned from git-fetch and the
 + * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
 + * appropriate arguments and returns its exit status.
 + */
 +static int run_rebase(const unsigned char *curr_head,
 +              const unsigned char *merge_head,
 +              const unsigned char *fork_point)
 +{
 +      int ret;
 +      unsigned char oct_merge_base[GIT_SHA1_RAWSZ];
 +      struct argv_array args = ARGV_ARRAY_INIT;
 +
 +      if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))
 +              if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))
 +                      fork_point = NULL;
 +
 +      argv_array_push(&args, "rebase");
 +
 +      /* Shared options */
 +      argv_push_verbosity(&args);
 +
 +      /* Options passed to git-rebase */
 +      if (opt_rebase == REBASE_PRESERVE)
 +              argv_array_push(&args, "--preserve-merges");
 +      if (opt_diffstat)
 +              argv_array_push(&args, opt_diffstat);
 +      argv_array_pushv(&args, opt_strategies.argv);
 +      argv_array_pushv(&args, opt_strategy_opts.argv);
 +      if (opt_gpg_sign)
 +              argv_array_push(&args, opt_gpg_sign);
 +
 +      argv_array_push(&args, "--onto");
 +      argv_array_push(&args, sha1_to_hex(merge_head));
 +
 +      if (fork_point && !is_null_sha1(fork_point))
 +              argv_array_push(&args, sha1_to_hex(fork_point));
 +      else
 +              argv_array_push(&args, sha1_to_hex(merge_head));
 +
 +      ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 +      argv_array_clear(&args);
 +      return ret;
 +}
 +
 +int cmd_pull(int argc, const char **argv, const char *prefix)
 +{
 +      const char *repo, **refspecs;
 +      struct sha1_array merge_heads = SHA1_ARRAY_INIT;
 +      unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];
 +      unsigned char rebase_fork_point[GIT_SHA1_RAWSZ];
 +
 +      if (!getenv("GIT_REFLOG_ACTION"))
 +              set_reflog_message(argc, argv);
 +
 +      argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 +
 +      parse_repo_refspecs(argc, argv, &repo, &refspecs);
 +
 +      if (!opt_ff)
 +              opt_ff = xstrdup_or_null(config_get_ff());
 +
 +      if (opt_rebase < 0)
 +              opt_rebase = config_get_rebase();
 +
 +      git_config(git_default_config, NULL);
 +
 +      if (read_cache_unmerged())
 +              die_resolve_conflict("Pull");
 +
 +      if (file_exists(git_path("MERGE_HEAD")))
 +              die_conclude_merge();
 +
 +      if (get_sha1("HEAD", orig_head))
 +              hashclr(orig_head);
 +
 +      if (opt_rebase) {
 +              int autostash = 0;
 +
 +              if (is_null_sha1(orig_head) && !is_cache_unborn())
 +                      die(_("Updating an unborn branch with changes added to the index."));
 +
 +              git_config_get_bool("rebase.autostash", &autostash);
 +              if (!autostash)
 +                      die_on_unclean_work_tree(prefix);
 +
 +              if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
 +                      hashclr(rebase_fork_point);
 +      }
 +
 +      if (run_fetch(repo, refspecs))
 +              return 1;
 +
 +      if (opt_dry_run)
 +              return 0;
 +
 +      if (get_sha1("HEAD", curr_head))
 +              hashclr(curr_head);
 +
 +      if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) &&
 +                      hashcmp(orig_head, curr_head)) {
 +              /*
 +               * The fetch involved updating the current branch.
 +               *
 +               * The working tree and the index file are still based on
 +               * orig_head commit, but we are merging into curr_head.
 +               * Update the working tree to match curr_head.
 +               */
 +
 +              warning(_("fetch updated the current branch head.\n"
 +                      "fast-forwarding your working tree from\n"
 +                      "commit %s."), sha1_to_hex(orig_head));
 +
 +              if (checkout_fast_forward(orig_head, curr_head, 0))
 +                      die(_("Cannot fast-forward your working tree.\n"
 +                              "After making sure that you saved anything precious from\n"
 +                              "$ git diff %s\n"
 +                              "output, run\n"
 +                              "$ git reset --hard\n"
 +                              "to recover."), sha1_to_hex(orig_head));
 +      }
 +
 +      get_merge_heads(&merge_heads);
 +
 +      if (!merge_heads.nr)
 +              die_no_merge_candidates(repo, refspecs);
 +
 +      if (is_null_sha1(orig_head)) {
 +              if (merge_heads.nr > 1)
 +                      die(_("Cannot merge multiple branches into empty head."));
 +              return pull_into_void(*merge_heads.sha1, curr_head);
 +      } else if (opt_rebase) {
 +              if (merge_heads.nr > 1)
 +                      die(_("Cannot rebase onto multiple branches."));
 +              return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);
 +      } else
 +              return run_merge();
 +}
diff --combined config.c
index 9fd275f2c2a46a0f8f5a78db49ba2c6f5635f32f,adf8b5303600abc53366c8463ad0b54718fefedb..a34b850cdc3f6902882c04f925456b4877978729
+++ b/config.c
@@@ -1939,8 -1939,6 +1939,8 @@@ int git_config_set_multivar_in_file(con
        int ret;
        struct lock_file *lock = NULL;
        char *filename_buf = NULL;
 +      char *contents = NULL;
 +      size_t contents_sz;
  
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
                        goto write_err_out;
        } else {
                struct stat st;
 -              char *contents;
 -              size_t contents_sz, copy_begin, copy_end;
 +              size_t copy_begin, copy_end;
                int i, new_line = 0;
  
                if (value_regex == NULL)
  
                fstat(in_fd, &st);
                contents_sz = xsize_t(st.st_size);
 -              contents = xmmap(NULL, contents_sz, PROT_READ,
 -                      MAP_PRIVATE, in_fd, 0);
 +              contents = xmmap_gently(NULL, contents_sz, PROT_READ,
 +                                      MAP_PRIVATE, in_fd, 0);
 +              if (contents == MAP_FAILED) {
 +                      if (errno == ENODEV && S_ISDIR(st.st_mode))
 +                              errno = EISDIR;
 +                      error("unable to mmap '%s': %s",
 +                            config_filename, strerror(errno));
 +                      ret = CONFIG_INVALID_FILE;
 +                      contents = NULL;
 +                      goto out_free;
 +              }
                close(in_fd);
  
-               if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+               if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
                        error("chmod on %s failed: %s",
-                               lock->filename.buf, strerror(errno));
+                             get_lock_file_path(lock), strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
                                goto write_err_out;
  
                munmap(contents, contents_sz);
 +              contents = NULL;
        }
  
        if (commit_lock_file(lock) < 0) {
@@@ -2146,12 -2135,10 +2146,12 @@@ out_free
        if (lock)
                rollback_lock_file(lock);
        free(filename_buf);
 +      if (contents)
 +              munmap(contents, contents_sz);
        return ret;
  
  write_err_out:
-       ret = write_error(lock->filename.buf);
+       ret = write_error(get_lock_file_path(lock));
        goto out_free;
  
  }
@@@ -2252,9 -2239,9 +2252,9 @@@ int git_config_rename_section_in_file(c
  
        fstat(fileno(config_file), &st);
  
-       if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+       if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
                ret = error("chmod on %s failed: %s",
-                               lock->filename.buf, strerror(errno));
+                           get_lock_file_path(lock), strerror(errno));
                goto out;
        }
  
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
-                                       ret = write_error(lock->filename.buf);
+                                       ret = write_error(get_lock_file_path(lock));
                                        goto out;
                                }
                                /*
                        continue;
                length = strlen(output);
                if (write_in_full(out_fd, output, length) != length) {
-                       ret = write_error(lock->filename.buf);
+                       ret = write_error(get_lock_file_path(lock));
                        goto out;
                }
        }
diff --combined diff.c
index 7deac90532234996088ae847b32ce07f5ba36339,528d25ca9c3ec8df316bf2cd331151d7a71d5dd3..976362a8ce0ae46160d44fd8bbabef4050769dc9
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -2,6 -2,7 +2,7 @@@
   * Copyright (C) 2005 Junio C Hamano
   */
  #include "cache.h"
+ #include "tempfile.h"
  #include "quote.h"
  #include "diff.h"
  #include "diffcore.h"
@@@ -42,7 -43,7 +43,7 @@@ static long diff_algorithm
  
  static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
 -      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_NORMAL,       /* CONTEXT */
        GIT_COLOR_BOLD,         /* METAINFO */
        GIT_COLOR_CYAN,         /* FRAGINFO */
        GIT_COLOR_RED,          /* OLD */
@@@ -54,8 -55,8 +55,8 @@@
  
  static int parse_diff_color_slot(const char *var)
  {
 -      if (!strcasecmp(var, "plain"))
 -              return DIFF_PLAIN;
 +      if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
 +              return DIFF_CONTEXT;
        if (!strcasecmp(var, "meta"))
                return DIFF_METAINFO;
        if (!strcasecmp(var, "frag"))
@@@ -308,11 -309,26 +309,26 @@@ static const char *external_diff(void
        return external_diff_cmd;
  }
  
+ /*
+  * Keep track of files used for diffing. Sometimes such an entry
+  * refers to a temporary file, sometimes to an existing file, and
+  * sometimes to "/dev/null".
+  */
  static struct diff_tempfile {
-       const char *name; /* filename external diff should read from */
+       /*
+        * filename external diff should read from, or NULL if this
+        * entry is currently not in use:
+        */
+       const char *name;
        char hex[41];
        char mode[10];
-       char tmp_path[PATH_MAX];
+       /*
+        * If this diff_tempfile instance refers to a temporary file,
+        * this tempfile object is used to manage its lifetime.
+        */
+       struct tempfile tempfile;
  } diff_temp[2];
  
  typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@@ -478,63 -494,30 +494,63 @@@ static int new_blank_line_at_eof(struc
        return ws_blank_line(line, len, ecbdata->ws_rule);
  }
  
 -static void emit_add_line(const char *reset,
 -                        struct emit_callback *ecbdata,
 -                        const char *line, int len)
 +static void emit_line_checked(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len,
 +                            enum color_diff color,
 +                            unsigned ws_error_highlight,
 +                            char sign)
  {
 -      const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 -      const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 +      const char *set = diff_get_color(ecbdata->color_diff, color);
 +      const char *ws = NULL;
  
 -      if (!*ws)
 -              emit_line_0(ecbdata->opt, set, reset, '+', line, len);
 -      else if (new_blank_line_at_eof(ecbdata, line, len))
 +      if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
 +              ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 +              if (!*ws)
 +                      ws = NULL;
 +      }
 +
 +      if (!ws)
 +              emit_line_0(ecbdata->opt, set, reset, sign, line, len);
 +      else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
                /* Blank line at EOF - paint '+' as well */
 -              emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
 +              emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
 -              emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
 +              emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
                ws_check_emit(line, len, ecbdata->ws_rule,
                              ecbdata->opt->file, set, reset, ws);
        }
  }
  
 +static void emit_add_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_NEW, WSEH_NEW, '+');
 +}
 +
 +static void emit_del_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_OLD, WSEH_OLD, '-');
 +}
 +
 +static void emit_context_line(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_CONTEXT, WSEH_CONTEXT, ' ');
 +}
 +
  static void emit_hunk_header(struct emit_callback *ecbdata,
                             const char *line, int len)
  {
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
 -              emit_line(ecbdata->opt, plain, reset, line, len);
 +              emit_line(ecbdata->opt, context, reset, line, len);
                return;
        }
        ep += 2; /* skip over @@ */
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
 -              strbuf_addstr(&msgbuf, plain);
 +              strbuf_addstr(&msgbuf, context);
                strbuf_add(&msgbuf, cp, ep - cp);
                strbuf_addstr(&msgbuf, reset);
        }
@@@ -597,25 -580,16 +613,16 @@@ static struct diff_tempfile *claim_diff
        die("BUG: diff is failing to clean up its tempfiles");
  }
  
- static int remove_tempfile_installed;
  static void remove_tempfile(void)
  {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-               if (diff_temp[i].name == diff_temp[i].tmp_path)
-                       unlink_or_warn(diff_temp[i].name);
+               if (is_tempfile_active(&diff_temp[i].tempfile))
+                       delete_tempfile(&diff_temp[i].tempfile);
                diff_temp[i].name = NULL;
        }
  }
  
- static void remove_tempfile_on_signal(int signo)
- {
-       remove_tempfile();
-       sigchain_pop(signo);
-       raise(signo);
- }
  static void print_line_count(FILE *file, int count)
  {
        switch (count) {
@@@ -636,6 -610,7 +643,6 @@@ static void emit_rewrite_lines(struct e
  {
        const char *endp = NULL;
        static const char *nneof = " No newline at end of file\n";
 -      const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
        const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
  
        while (0 < size) {
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
 -                      emit_line_0(ecb->opt, old, reset, '-',
 -                                  data, len);
 +                      emit_del_line(reset, ecb, data, len);
                } else {
                        ecb->lno_in_postimage++;
                        emit_add_line(reset, ecb, data, len);
                data += len;
        }
        if (!endp) {
 -              const char *plain = diff_get_color(ecb->color_diff,
 -                                                 DIFF_PLAIN);
 +              const char *context = diff_get_color(ecb->color_diff,
 +                                                   DIFF_CONTEXT);
                putc('\n', ecb->opt->file);
 -              emit_line_0(ecb->opt, plain, reset, '\\',
 +              emit_line_0(ecb->opt, context, reset, '\\',
                            nneof, strlen(nneof));
        }
  }
@@@ -1117,7 -1093,7 +1124,7 @@@ static void init_diff_words_data(struc
                struct diff_words_style *st = ecbdata->diff_words->style;
                st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
                st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
 -              st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
 +              st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
        }
  }
  
@@@ -1193,7 -1169,7 +1200,7 @@@ static void fn_out_consume(void *priv, 
  {
        struct emit_callback *ecbdata = priv;
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                        fputs("~\n", ecbdata->opt->file);
                } else {
                        /*
                              line++;
                              len--;
                        }
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                }
                return;
        }
  
 -      if (line[0] != '+') {
 -              const char *color =
 -                      diff_get_color(ecbdata->color_diff,
 -                                     line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
 -              ecbdata->lno_in_preimage++;
 -              if (line[0] == ' ')
 -                      ecbdata->lno_in_postimage++;
 -              emit_line(ecbdata->opt, color, reset, line, len);
 -      } else {
 +      switch (line[0]) {
 +      case '+':
                ecbdata->lno_in_postimage++;
                emit_add_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case '-':
 +              ecbdata->lno_in_preimage++;
 +              emit_del_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case ' ':
 +              ecbdata->lno_in_postimage++;
 +              ecbdata->lno_in_preimage++;
 +              emit_context_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      default:
 +              /* incomplete line at the end */
 +              ecbdata->lno_in_preimage++;
 +              emit_line(ecbdata->opt,
 +                        diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
 +                        reset, line, len);
 +              break;
        }
  }
  
@@@ -2858,8 -2824,7 +2865,7 @@@ static void prep_temp_blob(const char *
        strbuf_addstr(&template, "XXXXXX_");
        strbuf_addstr(&template, base);
  
-       fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
-                       strlen(base) + 1);
+       fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
        if (fd < 0)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
        }
        if (write_in_full(fd, blob, size) != size)
                die_errno("unable to write temp-file");
-       close(fd);
-       temp->name = temp->tmp_path;
+       close_tempfile(&temp->tempfile);
+       temp->name = get_tempfile_path(&temp->tempfile);
        strcpy(temp->hex, sha1_to_hex(sha1));
        temp->hex[40] = 0;
        sprintf(temp->mode, "%06o", mode);
@@@ -2895,12 -2860,6 +2901,6 @@@ static struct diff_tempfile *prepare_te
                return temp;
        }
  
-       if (!remove_tempfile_installed) {
-               atexit(remove_tempfile);
-               sigchain_push_common(remove_tempfile_on_signal);
-               remove_tempfile_installed = 1;
-       }
        if (!S_ISGITLINK(one->mode) &&
            (!one->sha1_valid ||
             reuse_worktree_file(name, one->sha1, 1))) {
@@@ -3264,7 -3223,6 +3264,7 @@@ void diff_setup(struct diff_options *op
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = diff_context_default;
 +      options->ws_error_highlight = WSEH_NEW;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
        /* pathchange left =NULL by default */
@@@ -3651,45 -3609,6 +3651,45 @@@ static void enable_patch_output(int *fm
        *fmt |= DIFF_FORMAT_PATCH;
  }
  
 +static int parse_one_token(const char **arg, const char *token)
 +{
 +      const char *rest;
 +      if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
 +              *arg = rest;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
 +{
 +      const char *orig_arg = arg;
 +      unsigned val = 0;
 +      while (*arg) {
 +              if (parse_one_token(&arg, "none"))
 +                      val = 0;
 +              else if (parse_one_token(&arg, "default"))
 +                      val = WSEH_NEW;
 +              else if (parse_one_token(&arg, "all"))
 +                      val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
 +              else if (parse_one_token(&arg, "new"))
 +                      val |= WSEH_NEW;
 +              else if (parse_one_token(&arg, "old"))
 +                      val |= WSEH_OLD;
 +              else if (parse_one_token(&arg, "context"))
 +                      val |= WSEH_CONTEXT;
 +              else {
 +                      error("unknown value after ws-error-highlight=%.*s",
 +                            (int)(arg - orig_arg), orig_arg);
 +                      return 0;
 +              }
 +              if (*arg)
 +                      arg++;
 +      }
 +      opt->ws_error_highlight = val;
 +      return 1;
 +}
 +
  int diff_opt_parse(struct diff_options *options, const char **av, int ac)
  {
        const char *arg = av[0];
                DIFF_OPT_SET(options, FIND_COPIES_HARDER);
        else if (!strcmp(arg, "--follow"))
                DIFF_OPT_SET(options, FOLLOW_RENAMES);
 -      else if (!strcmp(arg, "--no-follow"))
 +      else if (!strcmp(arg, "--no-follow")) {
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
 -      else if (!strcmp(arg, "--color"))
 +              DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
 +      } else if (!strcmp(arg, "--color"))
                options->use_color = 1;
        else if (skip_prefix(arg, "--color=", &arg)) {
                int value = git_config_colorbool(NULL, arg);
                DIFF_OPT_SET(options, SUBMODULE_LOG);
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
 +      else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
 +              return parse_ws_error_highlight(options, arg);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
diff --combined lockfile.c
index 993bb8274833651159cec6f0571b5b555ea073ca,e1d68f77cadc5d93f3bb5ae39f446d4390af2c59..637b8cf74317b029d19abddd5b307321a6b6e859
@@@ -1,38 -1,9 +1,9 @@@
  /*
   * Copyright (c) 2005, Junio C Hamano
   */
  #include "cache.h"
  #include "lockfile.h"
- #include "sigchain.h"
- static struct lock_file *volatile lock_file_list;
- static void remove_lock_files(int skip_fclose)
- {
-       pid_t me = getpid();
-       while (lock_file_list) {
-               if (lock_file_list->owner == me) {
-                       /* fclose() is not safe to call in a signal handler */
-                       if (skip_fclose)
-                               lock_file_list->fp = NULL;
-                       rollback_lock_file(lock_file_list);
-               }
-               lock_file_list = lock_file_list->next;
-       }
- }
- static void remove_lock_files_on_exit(void)
- {
-       remove_lock_files(0);
- }
- static void remove_lock_files_on_signal(int signo)
- {
-       remove_lock_files(1);
-       sigchain_pop(signo);
-       raise(signo);
- }
  
  /*
   * path = absolute or relative path name
@@@ -101,62 -72,27 +72,19 @@@ static void resolve_symlink(struct strb
  /* Make sure errno contains a meaningful value on error */
  static int lock_file(struct lock_file *lk, const char *path, int flags)
  {
-       size_t pathlen = strlen(path);
-       if (!lock_file_list) {
-               /* One-time initialization */
-               sigchain_push_common(remove_lock_files_on_signal);
-               atexit(remove_lock_files_on_exit);
-       }
-       if (lk->active)
-               die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
-                   path);
-       if (!lk->on_list) {
-               /* Initialize *lk and add it to lock_file_list: */
-               lk->fd = -1;
-               lk->fp = NULL;
-               lk->active = 0;
-               lk->owner = 0;
-               strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
-               lk->next = lock_file_list;
-               lock_file_list = lk;
-               lk->on_list = 1;
-       } else if (lk->filename.len) {
-               /* This shouldn't happen, but better safe than sorry. */
-               die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
-                   path);
-       }
+       int fd;
+       struct strbuf filename = STRBUF_INIT;
  
-       if (flags & LOCK_NO_DEREF) {
-               strbuf_add_absolute_path(&lk->filename, path);
-       } else {
-               struct strbuf resolved_path = STRBUF_INIT;
-               strbuf_add(&resolved_path, path, pathlen);
-               resolve_symlink(&resolved_path);
-               strbuf_add_absolute_path(&lk->filename, resolved_path.buf);
-               strbuf_release(&resolved_path);
-       }
+       strbuf_addstr(&filename, path);
+       if (!(flags & LOCK_NO_DEREF))
+               resolve_symlink(&filename);
  
-       strbuf_addstr(&lk->filename, LOCK_SUFFIX);
-       lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
-       if (lk->fd < 0) {
-               strbuf_reset(&lk->filename);
-               return -1;
-       }
-       lk->owner = getpid();
-       lk->active = 1;
-       if (adjust_shared_perm(lk->filename.buf)) {
-               int save_errno = errno;
-               error("cannot fix permission bits on %s", lk->filename.buf);
-               rollback_lock_file(lk);
-               errno = save_errno;
-               return -1;
-       }
-       return lk->fd;
+       strbuf_addstr(&filename, LOCK_SUFFIX);
+       fd = create_tempfile(&lk->tempfile, filename.buf);
+       strbuf_release(&filename);
+       return fd;
  }
  
 -static int sleep_microseconds(long us)
 -{
 -      struct timeval tv;
 -      tv.tv_sec = 0;
 -      tv.tv_usec = us;
 -      return select(0, NULL, NULL, NULL, &tv);
 -}
 -
  /*
   * Constants defining the gaps between attempts to lock a file. The
   * first backoff period is approximately INITIAL_BACKOFF_MS
@@@ -176,22 -112,27 +104,22 @@@ static int lock_file_timeout(struct loc
  {
        int n = 1;
        int multiplier = 1;
 -      long remaining_us = 0;
 +      long remaining_ms = 0;
        static int random_initialized = 0;
  
        if (timeout_ms == 0)
                return lock_file(lk, path, flags);
  
        if (!random_initialized) {
 -              srandom((unsigned int)getpid());
 +              srand((unsigned int)getpid());
                random_initialized = 1;
        }
  
 -      if (timeout_ms > 0) {
 -              /* avoid overflow */
 -              if (timeout_ms <= LONG_MAX / 1000)
 -                      remaining_us = timeout_ms * 1000;
 -              else
 -                      remaining_us = LONG_MAX;
 -      }
 +      if (timeout_ms > 0)
 +              remaining_ms = timeout_ms;
  
        while (1) {
 -              long backoff_ms, wait_us;
 +              long backoff_ms, wait_ms;
                int fd;
  
                fd = lock_file(lk, path, flags);
                        return fd; /* success */
                else if (errno != EEXIST)
                        return -1; /* failure other than lock held */
 -              else if (timeout_ms > 0 && remaining_us <= 0)
 +              else if (timeout_ms > 0 && remaining_ms <= 0)
                        return -1; /* failure due to timeout */
  
                backoff_ms = multiplier * INITIAL_BACKOFF_MS;
                /* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
 -              wait_us = (750 + random() % 500) * backoff_ms;
 -              sleep_microseconds(wait_us);
 -              remaining_us -= wait_us;
 +              wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
 +              sleep_millisec(wait_ms);
 +              remaining_ms -= wait_ms;
  
                /* Recursion: (n+1)^2 = n^2 + 2n + 1 */
                multiplier += 2*n + 1;
@@@ -287,116 -228,29 +215,29 @@@ int hold_lock_file_for_append(struct lo
        return fd;
  }
  
- FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
- {
-       if (!lk->active)
-               die("BUG: fdopen_lock_file() called for unlocked object");
-       if (lk->fp)
-               die("BUG: fdopen_lock_file() called twice for file '%s'", lk->filename.buf);
-       lk->fp = fdopen(lk->fd, mode);
-       return lk->fp;
- }
  char *get_locked_file_path(struct lock_file *lk)
  {
-       if (!lk->active)
-               die("BUG: get_locked_file_path() called for unlocked object");
-       if (lk->filename.len <= LOCK_SUFFIX_LEN)
-               die("BUG: get_locked_file_path() called for malformed lock object");
-       return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
- }
- int close_lock_file(struct lock_file *lk)
- {
-       int fd = lk->fd;
-       FILE *fp = lk->fp;
-       int err;
-       if (fd < 0)
-               return 0;
-       lk->fd = -1;
-       if (fp) {
-               lk->fp = NULL;
-               /*
-                * Note: no short-circuiting here; we want to fclose()
-                * in any case!
-                */
-               err = ferror(fp) | fclose(fp);
-       } else {
-               err = close(fd);
-       }
+       struct strbuf ret = STRBUF_INIT;
  
-       if (err) {
-               int save_errno = errno;
-               rollback_lock_file(lk);
-               errno = save_errno;
-               return -1;
-       }
-       return 0;
- }
- int reopen_lock_file(struct lock_file *lk)
- {
-       if (0 <= lk->fd)
-               die(_("BUG: reopen a lockfile that is still open"));
-       if (!lk->active)
-               die(_("BUG: reopen a lockfile that has been committed"));
-       lk->fd = open(lk->filename.buf, O_WRONLY);
-       return lk->fd;
+       strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
+       if (ret.len <= LOCK_SUFFIX_LEN ||
+           strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
+               die("BUG: get_locked_file_path() called for malformed lock object");
+       /* remove ".lock": */
+       strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
+       return strbuf_detach(&ret, NULL);
  }
  
- int commit_lock_file_to(struct lock_file *lk, const char *path)
+ int commit_lock_file(struct lock_file *lk)
  {
-       if (!lk->active)
-               die("BUG: attempt to commit unlocked object to \"%s\"", path);
+       char *result_path = get_locked_file_path(lk);
  
-       if (close_lock_file(lk))
-               return -1;
-       if (rename(lk->filename.buf, path)) {
+       if (commit_lock_file_to(lk, result_path)) {
                int save_errno = errno;
-               rollback_lock_file(lk);
+               free(result_path);
                errno = save_errno;
                return -1;
        }
-       lk->active = 0;
-       strbuf_reset(&lk->filename);
+       free(result_path);
        return 0;
  }
- int commit_lock_file(struct lock_file *lk)
- {
-       static struct strbuf result_file = STRBUF_INIT;
-       int err;
-       if (!lk->active)
-               die("BUG: attempt to commit unlocked object");
-       if (lk->filename.len <= LOCK_SUFFIX_LEN ||
-           strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
-               die("BUG: lockfile filename corrupt");
-       /* remove ".lock": */
-       strbuf_add(&result_file, lk->filename.buf,
-                  lk->filename.len - LOCK_SUFFIX_LEN);
-       err = commit_lock_file_to(lk, result_file.buf);
-       strbuf_reset(&result_file);
-       return err;
- }
- void rollback_lock_file(struct lock_file *lk)
- {
-       if (!lk->active)
-               return;
-       if (!close_lock_file(lk)) {
-               unlink_or_warn(lk->filename.buf);
-               lk->active = 0;
-               strbuf_reset(&lk->filename);
-       }
- }
diff --combined read-cache.c
index 89dbc0837a4155f9e70b56c7d61c8742482f3659,89be2268cd39fc87983b2bb66597e968f76e32cd..ab1bfc94d6eab530723d5fabe6093bb9929de35c
@@@ -5,6 -5,7 +5,7 @@@
   */
  #define NO_THE_INDEX_COMPATIBILITY_MACROS
  #include "cache.h"
+ #include "tempfile.h"
  #include "lockfile.h"
  #include "cache-tree.h"
  #include "refs.h"
@@@ -999,8 -1000,7 +1000,8 @@@ static int add_index_entry_with_check(s
        }
        pos = -pos-1;
  
 -      untracked_cache_add_to_index(istate, ce->name);
 +      if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
 +              untracked_cache_add_to_index(istate, ce->name);
  
        /*
         * Inserting a merged entry ("stage 0") into the index
@@@ -1563,7 -1563,7 +1564,7 @@@ int do_read_index(struct index_state *i
        if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
  
 -      mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
 +      mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (mmap == MAP_FAILED)
                die_errno("unable to map index file");
        close(fd);
@@@ -2113,7 -2113,7 +2114,7 @@@ static int commit_locked_index(struct l
  static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
  {
-       int ret = do_write_index(istate, lock->fd, 0);
+       int ret = do_write_index(istate, get_lock_file_fd(lock), 0);
        if (ret)
                return ret;
        assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
@@@ -2137,54 -2137,27 +2138,27 @@@ static int write_split_index(struct ind
        return ret;
  }
  
- static char *temporary_sharedindex;
- static void remove_temporary_sharedindex(void)
- {
-       if (temporary_sharedindex) {
-               unlink_or_warn(temporary_sharedindex);
-               free(temporary_sharedindex);
-               temporary_sharedindex = NULL;
-       }
- }
- static void remove_temporary_sharedindex_on_signal(int signo)
- {
-       remove_temporary_sharedindex();
-       sigchain_pop(signo);
-       raise(signo);
- }
+ static struct tempfile temporary_sharedindex;
  
  static int write_shared_index(struct index_state *istate,
                              struct lock_file *lock, unsigned flags)
  {
        struct split_index *si = istate->split_index;
-       static int installed_handler;
        int fd, ret;
  
-       temporary_sharedindex = git_pathdup("sharedindex_XXXXXX");
-       fd = mkstemp(temporary_sharedindex);
+       fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
        if (fd < 0) {
-               free(temporary_sharedindex);
-               temporary_sharedindex = NULL;
                hashclr(si->base_sha1);
                return do_write_locked_index(istate, lock, flags);
        }
-       if (!installed_handler) {
-               atexit(remove_temporary_sharedindex);
-               sigchain_push_common(remove_temporary_sharedindex_on_signal);
-       }
        move_cache_to_base_index(istate);
        ret = do_write_index(si->base, fd, 1);
-       close(fd);
        if (ret) {
-               remove_temporary_sharedindex();
+               delete_tempfile(&temporary_sharedindex);
                return ret;
        }
-       ret = rename(temporary_sharedindex,
-                    git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
-       free(temporary_sharedindex);
-       temporary_sharedindex = NULL;
+       ret = rename_tempfile(&temporary_sharedindex,
+                             git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
        if (!ret)
                hashcpy(si->base_sha1, si->base->sha1);
        return ret;
diff --combined refs.c
index b0e0fe560cebac68cb1a30b38307b3ac0094020d,bf68015b5d9376ce2809ecbd40ca1ab3ada2104f..4e15f60d98ea8affdef226bce199935fa694b195
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -19,14 -19,12 +19,14 @@@ struct ref_lock 
   * 1: End-of-component
   * 2: ., look for a preceding . to reject .. in refs
   * 3: {, look for a preceding @ to reject @{ in refs
 - * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
 + * 4: A bad character: ASCII control characters, and
 + *    ":", "?", "[", "\", "^", "~", SP, or TAB
 + * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
   */
  static unsigned char refname_disposition[256] = {
        1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 -      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
 +      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
   */
  #define REF_NEEDS_COMMIT 0x20
  
 +/*
 + * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
 + * value to ref_update::flags
 + */
 +
  /*
   * Try to read one refname component from the front of refname.
   * Return the length of the component found, or -1 if the component is
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
 - * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 - * - it ends with a "/".
 - * - it ends with ".lock"
 - * - it contains a "\" (backslash)
 + * - it has ASCII control characters, or
 + * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
 + * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
 + * - it ends with a "/", or
 + * - it ends with ".lock", or
 + * - it contains a "@{" portion
   */
 -static int check_refname_component(const char *refname, int flags)
 +static int check_refname_component(const char *refname, int *flags)
  {
        const char *cp;
        char last = '\0';
                        break;
                case 4:
                        return -1;
 +              case 5:
 +                      if (!(*flags & REFNAME_REFSPEC_PATTERN))
 +                              return -1; /* refspec can't be a pattern */
 +
 +                      /*
 +                       * Unset the pattern flag so that we only accept
 +                       * a single asterisk for one side of refspec.
 +                       */
 +                      *flags &= ~ REFNAME_REFSPEC_PATTERN;
 +                      break;
                }
                last = ch;
        }
@@@ -139,10 -120,18 +139,10 @@@ int check_refname_format(const char *re
  
        while (1) {
                /* We are at the start of a path component. */
 -              component_len = check_refname_component(refname, flags);
 -              if (component_len <= 0) {
 -                      if ((flags & REFNAME_REFSPEC_PATTERN) &&
 -                                      refname[0] == '*' &&
 -                                      (refname[1] == '\0' || refname[1] == '/')) {
 -                              /* Accept one wildcard as a full refname component. */
 -                              flags &= ~REFNAME_REFSPEC_PATTERN;
 -                              component_len = 1;
 -                      } else {
 -                              return -1;
 -                      }
 -              }
 +              component_len = check_refname_component(refname, &flags);
 +              if (component_len <= 0)
 +                      return -1;
 +
                component_count++;
                if (refname[component_len] == '\0')
                        break;
@@@ -1288,12 -1277,12 +1288,12 @@@ static void read_packed_refs(FILE *f, s
   */
  static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
  {
 -      const char *packed_refs_file;
 +      char *packed_refs_file;
  
        if (*refs->name)
 -              packed_refs_file = git_path_submodule(refs->name, "packed-refs");
 +              packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
        else
 -              packed_refs_file = git_path("packed-refs");
 +              packed_refs_file = git_pathdup("packed-refs");
  
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
                        fclose(f);
                }
        }
 +      free(packed_refs_file);
        return refs->packed;
  }
  
@@@ -1326,13 -1314,7 +1326,13 @@@ static struct ref_dir *get_packed_refs(
        return get_packed_ref_dir(get_packed_ref_cache(refs));
  }
  
 -void add_packed_ref(const char *refname, const unsigned char *sha1)
 +/*
 + * Add a reference to the in-memory packed reference cache.  This may
 + * only be called while the packed-refs file is locked (see
 + * lock_packed_refs()).  To actually write the packed-refs file, call
 + * commit_packed_refs().
 + */
 +static void add_packed_ref(const char *refname, const unsigned char *sha1)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -1352,23 -1334,19 +1352,23 @@@ static void read_loose_refs(const char 
  {
        struct ref_cache *refs = dir->ref_cache;
        DIR *d;
 -      const char *path;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
        struct strbuf refname;
 +      struct strbuf path = STRBUF_INIT;
 +      size_t path_baselen;
  
        if (*refs->name)
 -              path = git_path_submodule(refs->name, "%s", dirname);
 +              strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
        else
 -              path = git_path("%s", dirname);
 +              strbuf_git_path(&path, "%s", dirname);
 +      path_baselen = path.len;
  
 -      d = opendir(path);
 -      if (!d)
 +      d = opendir(path.buf);
 +      if (!d) {
 +              strbuf_release(&path);
                return;
 +      }
  
        strbuf_init(&refname, dirnamelen + 257);
        strbuf_add(&refname, dirname, dirnamelen);
                unsigned char sha1[20];
                struct stat st;
                int flag;
 -              const char *refdir;
  
                if (de->d_name[0] == '.')
                        continue;
                if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
 -              refdir = *refs->name
 -                      ? git_path_submodule(refs->name, "%s", refname.buf)
 -                      : git_path("%s", refname.buf);
 -              if (stat(refdir, &st) < 0) {
 +              strbuf_addstr(&path, de->d_name);
 +              if (stat(path.buf, &st) < 0) {
                        ; /* silently ignore */
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
 +                      int read_ok;
 +
                        if (*refs->name) {
                                hashclr(sha1);
                                flag = 0;
 -                              if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
 -                                      hashclr(sha1);
 -                                      flag |= REF_ISBROKEN;
 -                              }
 -                      } else if (read_ref_full(refname.buf,
 -                                               RESOLVE_REF_READING,
 -                                               sha1, &flag)) {
 +                              read_ok = !resolve_gitlink_ref(refs->name,
 +                                                             refname.buf, sha1);
 +                      } else {
 +                              read_ok = !read_ref_full(refname.buf,
 +                                                       RESOLVE_REF_READING,
 +                                                       sha1, &flag);
 +                      }
 +
 +                      if (!read_ok) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
 +                      } else if (is_null_sha1(sha1)) {
 +                              /*
 +                               * It is so astronomically unlikely
 +                               * that NULL_SHA1 is the SHA-1 of an
 +                               * actual object that we consider its
 +                               * appearance in a loose reference
 +                               * file to be repo corruption
 +                               * (probably due to a software bug).
 +                               */
 +                              flag |= REF_ISBROKEN;
                        }
 +
                        if (check_refname_format(refname.buf,
                                                 REFNAME_ALLOW_ONELEVEL)) {
                                if (!refname_is_safe(refname.buf))
                                         create_ref_entry(refname.buf, sha1, flag, 0));
                }
                strbuf_setlen(&refname, dirnamelen);
 +              strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
 +      strbuf_release(&path);
        closedir(d);
  }
  
@@@ -1485,15 -1449,14 +1485,15 @@@ static int resolve_gitlink_ref_recursiv
  {
        int fd, len;
        char buffer[128], *p;
 -      const char *path;
 +      char *path;
  
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
        path = *refs->name
 -              ? git_path_submodule(refs->name, "%s", refname)
 -              : git_path("%s", refname);
 +              ? git_pathdup_submodule(refs->name, "%s", refname)
 +              : git_pathdup("%s", refname);
        fd = open(path, O_RDONLY);
 +      free(path);
        if (fd < 0)
                return resolve_gitlink_packed_ref(refs, refname, sha1);
  
@@@ -1763,11 -1726,9 +1763,11 @@@ const char *resolve_ref_unsafe(const ch
        return ret;
  }
  
 -char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
 +char *resolve_refdup(const char *refname, int resolve_flags,
 +                   unsigned char *sha1, int *flags)
  {
 -      return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
 +      return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
 +                                                sha1, flags));
  }
  
  /* The argument to filter_refs */
@@@ -2146,8 -2107,7 +2146,8 @@@ int for_each_remote_ref_submodule(cons
  
  int for_each_replace_ref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 +      return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
 +                             strlen(git_replace_ref_base), 0, cb_data);
  }
  
  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@@ -2259,45 -2219,48 +2259,45 @@@ static void unlock_ref(struct ref_lock 
        free(lock);
  }
  
 -/* This function should make sure errno is meaningful on error */
 -static struct ref_lock *verify_lock(struct ref_lock *lock,
 -      const unsigned char *old_sha1, int mustexist)
 +/*
 + * Verify that the reference locked by lock has the value old_sha1.
 + * Fail if the reference doesn't exist and mustexist is set. Return 0
 + * on success. On error, write an error message to err, set errno, and
 + * return a negative value.
 + */
 +static int verify_lock(struct ref_lock *lock,
 +                     const unsigned char *old_sha1, int mustexist,
 +                     struct strbuf *err)
  {
 +      assert(err);
 +
        if (read_ref_full(lock->ref_name,
                          mustexist ? RESOLVE_REF_READING : 0,
                          lock->old_oid.hash, NULL)) {
                int save_errno = errno;
 -              error("Can't verify ref %s", lock->ref_name);
 -              unlock_ref(lock);
 +              strbuf_addf(err, "can't verify ref %s", lock->ref_name);
                errno = save_errno;
 -              return NULL;
 +              return -1;
        }
        if (hashcmp(lock->old_oid.hash, old_sha1)) {
 -              error("Ref %s is at %s but expected %s", lock->ref_name,
 -                      oid_to_hex(&lock->old_oid), sha1_to_hex(old_sha1));
 -              unlock_ref(lock);
 +              strbuf_addf(err, "ref %s is at %s but expected %s",
 +                          lock->ref_name,
 +                          sha1_to_hex(lock->old_oid.hash),
 +                          sha1_to_hex(old_sha1));
                errno = EBUSY;
 -              return NULL;
 +              return -1;
        }
 -      return lock;
 +      return 0;
  }
  
 -static int remove_empty_directories(const char *file)
 +static int remove_empty_directories(struct strbuf *path)
  {
 -      /* we want to create a file but there is a directory there;
 +      /*
 +       * we want to create a file but there is a directory there;
         * if that is an empty directory (or a directory that contains
         * only empty directories), remove them.
         */
 -      struct strbuf path;
 -      int result, save_errno;
 -
 -      strbuf_init(&path, 20);
 -      strbuf_addstr(&path, file);
 -
 -      result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
 -      save_errno = errno;
 -
 -      strbuf_release(&path);
 -      errno = save_errno;
 -
 -      return result;
 +      return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
  }
  
  /*
@@@ -2397,8 -2360,7 +2397,8 @@@ static struct ref_lock *lock_ref_sha1_b
                                            unsigned int flags, int *type_p,
                                            struct strbuf *err)
  {
 -      const char *ref_file;
 +      struct strbuf ref_file = STRBUF_INIT;
 +      struct strbuf orig_ref_file = STRBUF_INIT;
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
        refname = resolve_ref_unsafe(refname, resolve_flags,
                                     lock->old_oid.hash, &type);
        if (!refname && errno == EISDIR) {
 -              /* we are trying to lock foo but we used to
 +              /*
 +               * we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                 * it is normal for the empty directory 'foo'
                 * to remain.
                 */
 -              ref_file = git_path("%s", orig_refname);
 -              if (remove_empty_directories(ref_file)) {
 +              strbuf_git_path(&orig_ref_file, "%s", orig_refname);
 +              if (remove_empty_directories(&orig_ref_file)) {
                        last_errno = errno;
 -
                        if (!verify_refname_available(orig_refname, extras, skip,
                                                      get_loose_refs(&ref_cache), err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            orig_refname);
 -
                        goto error_return;
                }
                refname = resolve_ref_unsafe(orig_refname, resolve_flags,
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
 -      ref_file = git_path("%s", refname);
 +      strbuf_git_path(&ref_file, "%s", refname);
  
   retry:
 -      switch (safe_create_leading_directories_const(ref_file)) {
 +      switch (safe_create_leading_directories_const(ref_file.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                last_errno = errno;
 -              strbuf_addf(err, "unable to create directory for %s", ref_file);
 +              strbuf_addf(err, "unable to create directory for %s",
 +                          ref_file.buf);
                goto error_return;
        }
  
 -      if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
 +      if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
                last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         */
                        goto retry;
                else {
 -                      unable_to_lock_message(ref_file, errno, err);
 +                      unable_to_lock_message(ref_file.buf, errno, err);
                        goto error_return;
                }
        }
 -      return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 +      if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
 +              last_errno = errno;
 +              goto error_return;
 +      }
 +      goto out;
  
   error_return:
        unlock_ref(lock);
 +      lock = NULL;
 +
 + out:
 +      strbuf_release(&ref_file);
 +      strbuf_release(&orig_ref_file);
        errno = last_errno;
 -      return NULL;
 +      return lock;
  }
  
  /*
@@@ -2550,12 -2503,8 +2550,12 @@@ static int write_packed_entry_fn(struc
        return 0;
  }
  
 -/* This should return a meaningful errno on failure */
 -int lock_packed_refs(int flags)
 +/*
 + * Lock the packed-refs file for writing. Flags is passed to
 + * hold_lock_file_for_update(). Return 0 on success. On errors, set
 + * errno appropriately and return a nonzero value.
 + */
 +static int lock_packed_refs(int flags)
  {
        static int timeout_configured = 0;
        static int timeout_value = 1000;
  }
  
  /*
 - * Commit the packed refs changes.
 - * On error we must make sure that errno contains a meaningful value.
 + * Write the current version of the packed refs cache from memory to
 + * disk. The packed-refs file must already be locked for writing (see
 + * lock_packed_refs()). Return zero on success. On errors, set errno
 + * and return a nonzero value
   */
 -int commit_packed_refs(void)
 +static int commit_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        return error;
  }
  
 -void rollback_packed_refs(void)
 +/*
 + * Rollback the lockfile for the packed-refs file, and discard the
 + * in-memory packed reference cache.  (The packed-refs file will be
 + * read anew if it is needed again after this function is called.)
 + */
 +static void rollback_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -2782,14 -2724,7 +2782,14 @@@ int pack_refs(unsigned int flags
        return 0;
  }
  
 -int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 +/*
 + * Rewrite the packed-refs file, omitting any refs listed in
 + * 'refnames'. On error, leave packed-refs unchanged, write an error
 + * message to 'err', and return a nonzero value.
 + *
 + * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 + */
 +static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list_item *refname;
@@@ -2854,120 -2789,15 +2854,120 @@@ static int delete_ref_loose(struct ref_
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
 +static int is_per_worktree_ref(const char *refname)
 +{
 +      return !strcmp(refname, "HEAD");
 +}
 +
 +static int is_pseudoref_syntax(const char *refname)
 +{
 +      const char *c;
 +
 +      for (c = refname; *c; c++) {
 +              if (!isupper(*c) && *c != '-' && *c != '_')
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +enum ref_type ref_type(const char *refname)
 +{
 +      if (is_per_worktree_ref(refname))
 +              return REF_TYPE_PER_WORKTREE;
 +      if (is_pseudoref_syntax(refname))
 +              return REF_TYPE_PSEUDOREF;
 +       return REF_TYPE_NORMAL;
 +}
 +
 +static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
 +                         const unsigned char *old_sha1, struct strbuf *err)
 +{
 +      const char *filename;
 +      int fd;
 +      static struct lock_file lock;
 +      struct strbuf buf = STRBUF_INIT;
 +      int ret = -1;
 +
 +      strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 +
 +      filename = git_path("%s", pseudoref);
 +      fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
 +      if (fd < 0) {
 +              strbuf_addf(err, "Could not open '%s' for writing: %s",
 +                          filename, strerror(errno));
 +              return -1;
 +      }
 +
 +      if (old_sha1) {
 +              unsigned char actual_old_sha1[20];
 +
 +              if (read_ref(pseudoref, actual_old_sha1))
 +                      die("could not read ref '%s'", pseudoref);
 +              if (hashcmp(actual_old_sha1, old_sha1)) {
 +                      strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
 +                      rollback_lock_file(&lock);
 +                      goto done;
 +              }
 +      }
 +
 +      if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
 +              strbuf_addf(err, "Could not write to '%s'", filename);
 +              rollback_lock_file(&lock);
 +              goto done;
 +      }
 +
 +      commit_lock_file(&lock);
 +      ret = 0;
 +done:
 +      strbuf_release(&buf);
 +      return ret;
 +}
 +
 +static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
 +{
 +      static struct lock_file lock;
 +      const char *filename;
 +
 +      filename = git_path("%s", pseudoref);
 +
 +      if (old_sha1 && !is_null_sha1(old_sha1)) {
 +              int fd;
 +              unsigned char actual_old_sha1[20];
 +
 +              fd = hold_lock_file_for_update(&lock, filename,
 +                                             LOCK_DIE_ON_ERROR);
 +              if (fd < 0)
 +                      die_errno(_("Could not open '%s' for writing"), filename);
 +              if (read_ref(pseudoref, actual_old_sha1))
 +                      die("could not read ref '%s'", pseudoref);
 +              if (hashcmp(actual_old_sha1, old_sha1)) {
 +                      warning("Unexpected sha1 when deleting %s", pseudoref);
 +                      rollback_lock_file(&lock);
 +                      return -1;
 +              }
 +
 +              unlink(filename);
 +              rollback_lock_file(&lock);
 +      } else {
 +              unlink(filename);
 +      }
 +
 +      return 0;
 +}
 +
 +int delete_ref(const char *refname, const unsigned char *old_sha1,
 +             unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
 +      if (ref_type(refname) == REF_TYPE_PSEUDOREF)
 +              return delete_pseudoref(refname, old_sha1);
 +
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_delete(transaction, refname,
 -                                 (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
 +          ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, NULL, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
        return 0;
  }
  
 +int delete_refs(struct string_list *refnames)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      int i, result = 0;
 +
 +      if (!refnames->nr)
 +              return 0;
 +
 +      result = repack_without_refs(refnames, &err);
 +      if (result) {
 +              /*
 +               * If we failed to rewrite the packed-refs file, then
 +               * it is unsafe to try to remove loose refs, because
 +               * doing so might expose an obsolete packed value for
 +               * a reference that might even point at an object that
 +               * has been garbage collected.
 +               */
 +              if (refnames->nr == 1)
 +                      error(_("could not delete reference %s: %s"),
 +                            refnames->items[0].string, err.buf);
 +              else
 +                      error(_("could not delete references: %s"), err.buf);
 +
 +              goto out;
 +      }
 +
 +      for (i = 0; i < refnames->nr; i++) {
 +              const char *refname = refnames->items[i].string;
 +
 +              if (delete_ref(refname, NULL, 0))
 +                      result |= error(_("could not remove reference %s"), refname);
 +      }
 +
 +out:
 +      strbuf_release(&err);
 +      return result;
 +}
 +
  /*
   * People using contrib's git-new-workdir have .git/logs/refs ->
   * /some/other/path/.git/logs/refs, and that may live on another device.
  static int rename_tmp_log(const char *newrefname)
  {
        int attempts_remaining = 4;
 +      struct strbuf path = STRBUF_INIT;
 +      int ret = -1;
  
   retry:
 -      switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
 +      strbuf_reset(&path);
 +      strbuf_git_path(&path, "logs/%s", newrefname);
 +      switch (safe_create_leading_directories_const(path.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                error("unable to create directory for %s", newrefname);
 -              return -1;
 +              goto out;
        }
  
 -      if (rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
 +      if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
                if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
                        /*
                         * rename(a, b) when b is an existing
                         * directory ought to result in ISDIR, but
                         * Solaris 5.8 gives ENOTDIR.  Sheesh.
                         */
 -                      if (remove_empty_directories(git_path("logs/%s", newrefname))) {
 +                      if (remove_empty_directories(&path)) {
                                error("Directory not empty: logs/%s", newrefname);
 -                              return -1;
 +                              goto out;
                        }
                        goto retry;
                } else if (errno == ENOENT && --attempts_remaining > 0) {
                } else {
                        error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
                                newrefname, strerror(errno));
 -                      return -1;
 +                      goto out;
                }
        }
 -      return 0;
 +      ret = 0;
 +out:
 +      strbuf_release(&path);
 +      return ret;
  }
  
  static int rename_ref_available(const char *oldname, const char *newname)
        return ret;
  }
  
 -static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
 +static int write_ref_to_lockfile(struct ref_lock *lock,
 +                               const unsigned char *sha1, struct strbuf *err);
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg);
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err);
  
  int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
        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))) {
 +                      struct strbuf path = STRBUF_INIT;
 +                      int result;
 +
 +                      strbuf_git_path(&path, "%s", newrefname);
 +                      result = remove_empty_directories(&path);
 +                      strbuf_release(&path);
 +
 +                      if (result) {
                                error("Directory not empty: %s", newrefname);
                                goto rollback;
                        }
        }
        hashcpy(lock->old_oid.hash, orig_sha1);
  
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, logmsg)) {
 -              error("unable to write current sha1 into %s", newrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", newrefname, err.buf);
 +              strbuf_release(&err);
                goto rollback;
        }
  
  
        flag = log_all_ref_updates;
        log_all_ref_updates = 0;
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, NULL))
 -              error("unable to write current sha1 into %s", oldrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
 +              strbuf_release(&err);
 +      }
        log_all_ref_updates = flag;
  
   rollbacklog:
@@@ -3248,73 -3021,60 +3248,73 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
 -/* This function must set a meaningful errno on failure */
 -int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
 +static int should_autocreate_reflog(const char *refname)
 +{
 +      if (!log_all_ref_updates)
 +              return 0;
 +      return starts_with(refname, "refs/heads/") ||
 +              starts_with(refname, "refs/remotes/") ||
 +              starts_with(refname, "refs/notes/") ||
 +              !strcmp(refname, "HEAD");
 +}
 +
 +/*
 + * Create a reflog for a ref.  If force_create = 0, the reflog will
 + * only be created for certain refs (those for which
 + * should_autocreate_reflog returns non-zero.  Otherwise, create it
 + * regardless of the ref name.  Fill in *err and return -1 on failure.
 + */
 +static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
 -      char *logfile;
 -
 -      strbuf_git_path(sb_logfile, "logs/%s", refname);
 -      logfile = sb_logfile->buf;
 -      /* make sure the rest of the function can't change "logfile" */
 -      sb_logfile = NULL;
 -      if (log_all_ref_updates &&
 -          (starts_with(refname, "refs/heads/") ||
 -           starts_with(refname, "refs/remotes/") ||
 -           starts_with(refname, "refs/notes/") ||
 -           !strcmp(refname, "HEAD"))) {
 -              if (safe_create_leading_directories(logfile) < 0) {
 -                      int save_errno = errno;
 -                      error("unable to create directory for %s", logfile);
 -                      errno = save_errno;
 +
 +      strbuf_git_path(logfile, "logs/%s", refname);
 +      if (force_create || should_autocreate_reflog(refname)) {
 +              if (safe_create_leading_directories(logfile->buf) < 0) {
 +                      strbuf_addf(err, "unable to create directory for %s: "
 +                                  "%s", logfile->buf, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
        }
  
 -      logfd = open(logfile, oflags, 0666);
 +      logfd = open(logfile->buf, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
                        return 0;
  
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
 -                              int save_errno = errno;
 -                              error("There are still logs under '%s'",
 -                                    logfile);
 -                              errno = save_errno;
 +                              strbuf_addf(err, "There are still logs under "
 +                                          "'%s'", logfile->buf);
                                return -1;
                        }
 -                      logfd = open(logfile, oflags, 0666);
 +                      logfd = open(logfile->buf, oflags, 0666);
                }
  
                if (logfd < 0) {
 -                      int save_errno = errno;
 -                      error("Unable to append to %s: %s", logfile,
 -                            strerror(errno));
 -                      errno = save_errno;
 +                      strbuf_addf(err, "unable to append to %s: %s",
 +                                  logfile->buf, strerror(errno));
                        return -1;
                }
        }
  
 -      adjust_shared_perm(logfile);
 +      adjust_shared_perm(logfile->buf);
        close(logfd);
        return 0;
  }
  
 +
 +int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
 +{
 +      int ret;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      ret = log_ref_setup(refname, &sb, err, force_create);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
  static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
                            const unsigned char *new_sha1,
                            const char *committer, const char *msg)
  
  static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
                           const unsigned char *new_sha1, const char *msg,
 -                         struct strbuf *sb_log_file)
 +                         struct strbuf *logfile, int flags,
 +                         struct strbuf *err)
  {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
 -      char *log_file;
  
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
 -      result = log_ref_setup(refname, sb_log_file);
 +      result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
 +
        if (result)
                return result;
 -      log_file = sb_log_file->buf;
 -      /* make sure the rest of the function can't change "log_file" */
 -      sb_log_file = NULL;
  
 -      logfd = open(log_file, oflags);
 +      logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 0;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
 -              int save_errno = errno;
 +              strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
 +                          strerror(errno));
                close(logfd);
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
                return -1;
        }
        if (close(logfd)) {
 -              int save_errno = errno;
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
 +              strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
 +                          strerror(errno));
                return -1;
        }
        return 0;
  }
  
  static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 -                       const unsigned char *new_sha1, const char *msg)
 +                       const unsigned char *new_sha1, const char *msg,
 +                       int flags, struct strbuf *err)
  {
        struct strbuf sb = STRBUF_INIT;
 -      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
 +      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
 +                                err);
        strbuf_release(&sb);
        return ret;
  }
@@@ -3393,36 -3155,38 +3393,38 @@@ int is_branch(const char *refname
  
  /*
   * Write sha1 into the open lockfile, then close the lockfile. On
 - * errors, rollback the lockfile and set errno to reflect the problem.
 + * errors, rollback the lockfile, fill in *err and
 + * return -1.
   */
  static int write_ref_to_lockfile(struct ref_lock *lock,
 -                               const unsigned char *sha1)
 +                               const unsigned char *sha1, struct strbuf *err)
  {
        static char term = '\n';
        struct object *o;
+       int fd;
  
        o = parse_object(sha1);
        if (!o) {
 -              error("Trying to write ref %s with nonexistent object %s",
 -                      lock->ref_name, sha1_to_hex(sha1));
 +              strbuf_addf(err,
 +                          "Trying to write ref %s with nonexistent object %s",
 +                          lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
 -              error("Trying to write non-commit object %s to branch %s",
 -                      sha1_to_hex(sha1), lock->ref_name);
 +              strbuf_addf(err,
 +                          "Trying to write non-commit object %s to branch %s",
 +                          sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
-       if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
-           write_in_full(lock->lk->fd, &term, 1) != 1 ||
+       fd = get_lock_file_fd(lock->lk);
+       if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
+           write_in_full(fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
 -              int save_errno = errno;
 -              error("Couldn't write %s", get_lock_file_path(lock->lk));
 +              strbuf_addf(err,
-                           "Couldn't write %s", lock->lk->filename.buf);
++                          "Couldn't write %s", get_lock_file_path(lock->lk));
                unlock_ref(lock);
 -              errno = save_errno;
                return -1;
        }
        return 0;
   * necessary, using the specified lockmsg (which can be NULL).
   */
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg)
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err)
  {
        clear_loose_ref_cache(&ref_cache);
 -      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
 +      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
            (strcmp(lock->ref_name, lock->orig_ref_name) &&
 -           log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
 +           log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
 +              char *old_msg = strbuf_detach(err, NULL);
 +              strbuf_addf(err, "Cannot update the ref '%s': %s",
 +                          lock->ref_name, old_msg);
 +              free(old_msg);
                unlock_ref(lock);
                return -1;
        }
                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_oid.hash, sha1, logmsg);
 +                  !strcmp(head_ref, lock->ref_name)) {
 +                      struct strbuf log_err = STRBUF_INIT;
 +                      if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
 +                                        logmsg, 0, &log_err)) {
 +                              error("%s", log_err.buf);
 +                              strbuf_release(&log_err);
 +                      }
 +              }
        }
        if (commit_ref(lock)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
 +
        unlock_ref(lock);
        return 0;
  }
  int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
  {
 -      const char *lockpath;
 +      char *lockpath = NULL;
        char ref[1000];
        int fd, len, written;
        char *git_HEAD = git_pathdup("%s", ref_target);
        unsigned char old_sha1[20], new_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
        if (logmsg && read_ref(ref_target, old_sha1))
                hashclr(old_sha1);
                error("refname too long: %s", refs_heads_master);
                goto error_free_return;
        }
 -      lockpath = mkpath("%s.lock", git_HEAD);
 +      lockpath = mkpathdup("%s.lock", git_HEAD);
        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
        if (fd < 0) {
                error("Unable to open %s for writing", lockpath);
        error_unlink_return:
                unlink_or_warn(lockpath);
        error_free_return:
 +              free(lockpath);
                free(git_HEAD);
                return -1;
        }
 +      free(lockpath);
  
  #ifndef NO_SYMLINK_HEAD
        done:
  #endif
 -      if (logmsg && !read_ref(refs_heads_master, new_sha1))
 -              log_ref_write(ref_target, old_sha1, new_sha1, logmsg);
 +      if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
 +              log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +      }
  
        free(git_HEAD);
        return 0;
@@@ -3600,14 -3346,14 +3602,14 @@@ static int read_ref_at_ent(unsigned cha
                        hashcpy(cb->sha1, nsha1);
                        if (hashcmp(cb->osha1, nsha1))
                                warning("Log for ref %s has gap after %s.",
 -                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
                        hashcpy(cb->sha1, nsha1);
                else if (hashcmp(nsha1, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
 -                                                 DATE_RFC2822));
 +                                                     DATE_MODE(RFC2822)));
                hashcpy(cb->osha1, osha1);
                hashcpy(cb->nsha1, nsha1);
                cb->found_it = 1;
@@@ -4066,25 -3812,17 +4068,25 @@@ int update_ref(const char *msg, const c
               const unsigned char *new_sha1, const unsigned char *old_sha1,
               unsigned int flags, enum action_on_err onerr)
  {
 -      struct ref_transaction *t;
 +      struct ref_transaction *t = NULL;
        struct strbuf err = STRBUF_INIT;
 +      int ret = 0;
  
 -      t = ref_transaction_begin(&err);
 -      if (!t ||
 -          ref_transaction_update(t, refname, new_sha1, old_sha1,
 -                                 flags, msg, &err) ||
 -          ref_transaction_commit(t, &err)) {
 +      if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
 +              ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
 +      } else {
 +              t = ref_transaction_begin(&err);
 +              if (!t ||
 +                  ref_transaction_update(t, refname, new_sha1, old_sha1,
 +                                         flags, msg, &err) ||
 +                  ref_transaction_commit(t, &err)) {
 +                      ret = 1;
 +                      ref_transaction_free(t);
 +              }
 +      }
 +      if (ret) {
                const char *str = "update_ref failed for ref '%s': %s";
  
 -              ref_transaction_free(t);
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR:
                        error(str, refname, err.buf);
                return 1;
        }
        strbuf_release(&err);
 -      ref_transaction_free(t);
 +      if (t)
 +              ref_transaction_free(t);
        return 0;
  }
  
@@@ -4177,7 -3914,7 +4179,7 @@@ int ref_transaction_commit(struct ref_t
                                ? TRANSACTION_NAME_CONFLICT
                                : TRANSACTION_GENERIC_ERROR;
                        reason = strbuf_detach(err, NULL);
 -                      strbuf_addf(err, "Cannot lock ref '%s': %s",
 +                      strbuf_addf(err, "cannot lock ref '%s': %s",
                                    update->refname, reason);
                        free(reason);
                        goto cleanup;
                                 * value, so we don't need to write it.
                                 */
                        } else if (write_ref_to_lockfile(update->lock,
 -                                                       update->new_sha1)) {
 +                                                       update->new_sha1,
 +                                                       err)) {
 +                              char *write_err = strbuf_detach(err, NULL);
 +
                                /*
                                 * The lock was freed upon failure of
                                 * write_ref_to_lockfile():
                                 */
                                update->lock = NULL;
 -                              strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                          update->refname);
 +                              strbuf_addf(err,
 +                                          "cannot update the ref '%s': %s",
 +                                          update->refname, write_err);
 +                              free(write_err);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
  
                if (update->flags & REF_NEEDS_COMMIT) {
                        if (commit_ref_update(update->lock,
 -                                            update->new_sha1, update->msg)) {
 +                                            update->new_sha1, update->msg,
 +                                            update->flags, err)) {
                                /* freed by commit_ref_update(): */
                                update->lock = NULL;
 -                              strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                          update->refname);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
@@@ -4280,98 -4013,6 +4282,98 @@@ cleanup
        return ret;
  }
  
 +static int ref_present(const char *refname,
 +                     const struct object_id *oid, int flags, void *cb_data)
 +{
 +      struct string_list *affected_refnames = cb_data;
 +
 +      return string_list_has_string(affected_refnames, refname);
 +}
 +
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err)
 +{
 +      struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
 +      struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
 +      int ret = 0, i;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
 +      struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 +
 +      assert(err);
 +
 +      if (transaction->state != REF_TRANSACTION_OPEN)
 +              die("BUG: commit called for transaction that is not open");
 +
 +      /* Fail if a refname appears more than once in the transaction: */
 +      for (i = 0; i < n; i++)
 +              string_list_append(&affected_refnames, updates[i]->refname);
 +      string_list_sort(&affected_refnames);
 +      if (ref_update_reject_duplicates(&affected_refnames, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      /*
 +       * It's really undefined to call this function in an active
 +       * repository or when there are existing references: we are
 +       * only locking and changing packed-refs, so (1) any
 +       * simultaneous processes might try to change a reference at
 +       * the same time we do, and (2) any existing loose versions of
 +       * the references that we are setting would have precedence
 +       * over our values. But some remote helpers create the remote
 +       * "HEAD" and "master" branches before calling this function,
 +       * so here we really only check that none of the references
 +       * that we are creating already exists.
 +       */
 +      if (for_each_rawref(ref_present, &affected_refnames))
 +              die("BUG: initial ref transaction called with existing refs");
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_OLD) &&
 +                  !is_null_sha1(update->old_sha1))
 +                      die("BUG: initial ref transaction with old_sha1 set");
 +              if (verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           loose_refs, err) ||
 +                  verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           packed_refs, err)) {
 +                      ret = TRANSACTION_NAME_CONFLICT;
 +                      goto cleanup;
 +              }
 +      }
 +
 +      if (lock_packed_refs(0)) {
 +              strbuf_addf(err, "unable to lock packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_NEW) &&
 +                  !is_null_sha1(update->new_sha1))
 +                      add_packed_ref(update->refname, update->new_sha1);
 +      }
 +
 +      if (commit_packed_refs()) {
 +              strbuf_addf(err, "unable to commit packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +cleanup:
 +      transaction->state = REF_TRANSACTION_CLOSED;
 +      string_list_clear(&affected_refnames, 0);
 +      return ret;
 +}
 +
  char *shorten_unambiguous_ref(const char *refname, int strict)
  {
        int i;
@@@ -4493,25 -4134,17 +4495,25 @@@ int parse_hide_refs_config(const char *
  
  int ref_is_hidden(const char *refname)
  {
 -      struct string_list_item *item;
 +      int i;
  
        if (!hide_refs)
                return 0;
 -      for_each_string_list_item(item, hide_refs) {
 +      for (i = hide_refs->nr - 1; i >= 0; i--) {
 +              const char *match = hide_refs->items[i].string;
 +              int neg = 0;
                int len;
 -              if (!starts_with(refname, item->string))
 +
 +              if (*match == '!') {
 +                      neg = 1;
 +                      match++;
 +              }
 +
 +              if (!starts_with(refname, match))
                        continue;
 -              len = strlen(item->string);
 +              len = strlen(match);
                if (!refname[len] || refname[len] == '/')
 -                      return 1;
 +                      return !neg;
        }
        return 0;
  }
@@@ -4608,7 -4241,7 +4610,7 @@@ int reflog_expire(const char *refname, 
                cb.newlog = fdopen_lock_file(&reflog_lock, "w");
                if (!cb.newlog) {
                        error("cannot fdopen %s (%s)",
-                             reflog_lock.filename.buf, strerror(errno));
+                             get_lock_file_path(&reflog_lock), strerror(errno));
                        goto failure;
                }
        }
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
                } else if (update &&
-                          (write_in_full(lock->lk->fd,
+                          (write_in_full(get_lock_file_fd(lock->lk),
                                sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
-                        write_str_in_full(lock->lk->fd, "\n") != 1 ||
-                        close_ref(lock) < 0)) {
+                           write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
+                           close_ref(lock) < 0)) {
                        status |= error("couldn't write %s",
-                                       lock->lk->filename.buf);
+                                       get_lock_file_path(lock->lk));
                        rollback_lock_file(&reflog_lock);
                } else if (commit_lock_file(&reflog_lock)) {
                        status |= error("unable to commit reflog '%s' (%s)",
diff --combined shallow.c
index 4ded4a8cde45e01bdd2d551ccf45226e82f1ff4c,2ba29a5ef56351b91e0cf73f75c58ccb21522581..d49a3d6e9f02e292a04981e34f0a2d0b1ffa00d0
+++ b/shallow.c
@@@ -1,4 -1,5 +1,5 @@@
  #include "cache.h"
+ #include "tempfile.h"
  #include "lockfile.h"
  #include "commit.h"
  #include "tag.h"
@@@ -48,7 -49,7 +49,7 @@@ int is_repository_shallow(void
                return is_shallow;
  
        if (!path)
 -              path = git_path("shallow");
 +              path = git_path_shallow();
        /*
         * fetch-pack sets '--shallow-file ""' as an indicator that no
         * shallow file should be used. We could just open it and it
@@@ -142,7 -143,7 +143,7 @@@ static void check_shallow_file_for_upda
        if (is_shallow == -1)
                die("BUG: shallow must be initialized by now");
  
 -      if (!stat_validity_check(&shallow_stat, git_path("shallow")))
 +      if (!stat_validity_check(&shallow_stat, git_path_shallow()))
                die("shallow file has changed since we read it");
  }
  
@@@ -208,50 -209,28 +209,28 @@@ int write_shallow_commits(struct strbu
        return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
  }
  
- static struct strbuf temporary_shallow = STRBUF_INIT;
- static void remove_temporary_shallow(void)
- {
-       if (temporary_shallow.len) {
-               unlink_or_warn(temporary_shallow.buf);
-               strbuf_reset(&temporary_shallow);
-       }
- }
- static void remove_temporary_shallow_on_signal(int signo)
- {
-       remove_temporary_shallow();
-       sigchain_pop(signo);
-       raise(signo);
- }
+ static struct tempfile temporary_shallow;
  
  const char *setup_temporary_shallow(const struct sha1_array *extra)
  {
        struct strbuf sb = STRBUF_INIT;
        int fd;
  
-       if (temporary_shallow.len)
-               die("BUG: attempt to create two temporary shallow files");
        if (write_shallow_commits(&sb, 0, extra)) {
-               strbuf_addstr(&temporary_shallow, git_path("shallow_XXXXXX"));
-               fd = xmkstemp(temporary_shallow.buf);
-               atexit(remove_temporary_shallow);
-               sigchain_push_common(remove_temporary_shallow_on_signal);
+               fd = xmks_tempfile(&temporary_shallow, git_path("shallow_XXXXXX"));
  
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
-                                 temporary_shallow.buf);
-               close(fd);
+                                 get_tempfile_path(&temporary_shallow));
+               close_tempfile(&temporary_shallow);
                strbuf_release(&sb);
-               return temporary_shallow.buf;
+               return get_tempfile_path(&temporary_shallow);
        }
        /*
         * is_repository_shallow() sees empty string as "no shallow
         * file".
         */
-       return temporary_shallow.buf;
+       return get_tempfile_path(&temporary_shallow);
  }
  
  void setup_alternate_shallow(struct lock_file *shallow_lock,
        struct strbuf sb = STRBUF_INIT;
        int fd;
  
 -      fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
 +      fd = hold_lock_file_for_update(shallow_lock, git_path_shallow(),
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update();
        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.buf);
-               *alternate_shallow_file = shallow_lock->filename.buf;
+                                 get_lock_file_path(shallow_lock));
+               *alternate_shallow_file = get_lock_file_path(shallow_lock);
        } else
                /*
                 * is_repository_shallow() sees empty string as "no
@@@ -308,16 -287,16 +287,16 @@@ void prune_shallow(int show_only
                strbuf_release(&sb);
                return;
        }
 -      fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
 +      fd = hold_lock_file_for_update(&shallow_lock, git_path_shallow(),
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update();
        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.buf);
+                                 get_lock_file_path(&shallow_lock));
                commit_lock_file(&shallow_lock);
        } else {
 -              unlink(git_path("shallow"));
 +              unlink(git_path_shallow());
                rollback_lock_file(&shallow_lock);
        }
        strbuf_release(&sb);