Merge branch 'nd/error-errno'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 May 2016 21:38:28 +0000 (14:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 May 2016 21:38:28 +0000 (14:38 -0700)
The code for warning_errno/die_errno has been refactored and a new
error_errno() reporting helper is introduced.

* nd/error-errno: (41 commits)
wrapper.c: use warning_errno()
vcs-svn: use error_errno()
upload-pack.c: use error_errno()
unpack-trees.c: use error_errno()
transport-helper.c: use error_errno()
sha1_file.c: use {error,die,warning}_errno()
server-info.c: use error_errno()
sequencer.c: use error_errno()
run-command.c: use error_errno()
rerere.c: use error_errno() and warning_errno()
reachable.c: use error_errno()
mailmap.c: use error_errno()
ident.c: use warning_errno()
http.c: use error_errno() and warning_errno()
grep.c: use error_errno()
gpg-interface.c: use error_errno()
fast-import.c: use error_errno()
entry.c: use error_errno()
editor.c: use error_errno()
diff-no-index.c: use error_errno()
...

18 files changed:
1  2 
builtin/branch.c
builtin/fetch.c
builtin/pack-objects.c
builtin/rm.c
builtin/worktree.c
config.c
credential-cache--daemon.c
git-compat-util.h
gpg-interface.c
http.c
ident.c
mailmap.c
rerere.c
run-command.c
transport-helper.c
unpack-trees.c
upload-pack.c
wrapper.c
diff --combined builtin/branch.c
index 37af77161e4f899f9897db0838f0b736b8450bec,6045dca4714ce6ed90912dd92c6f7560fb74da50..eacec57593fda5dc15c611f0e92b4a6614c5aa00
@@@ -20,7 -20,6 +20,7 @@@
  #include "utf8.h"
  #include "wt-status.h"
  #include "ref-filter.h"
 +#include "worktree.h"
  
  static const char * const builtin_branch_usage[] = {
        N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
@@@ -216,21 -215,16 +216,21 @@@ static int delete_branches(int argc, co
                int flags = 0;
  
                strbuf_branchname(&bname, argv[i]);
 -              if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) {
 -                      error(_("Cannot delete the branch '%s' "
 -                            "which you are currently on."), bname.buf);
 -                      ret = 1;
 -                      continue;
 -              }
 -
                free(name);
 -
                name = mkpathdup(fmt, bname.buf);
 +
 +              if (kinds == FILTER_REFS_BRANCHES) {
 +                      char *worktree = find_shared_symref("HEAD", name);
 +                      if (worktree) {
 +                              error(_("Cannot delete branch '%s' "
 +                                      "checked out at '%s'"),
 +                                    bname.buf, worktree);
 +                              free(worktree);
 +                              ret = 1;
 +                              continue;
 +                      }
 +              }
 +
                target = resolve_ref_unsafe(name,
                                            RESOLVE_REF_READING
                                            | RESOLVE_REF_NO_RECURSE
@@@ -375,14 -369,12 +375,14 @@@ static char *get_head_description(void
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
 -              /* TRANSLATORS: make sure these match _("HEAD detached at ")
 -                 and _("HEAD detached from ") in wt-status.c */
                if (state.detached_at)
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached at " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached from " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@@ -401,25 -393,22 +401,25 @@@ static void format_and_print_ref_item(s
        int current = 0;
        int color;
        struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 -      const char *prefix = "";
 +      const char *prefix_to_show = "";
 +      const char *prefix_to_skip = NULL;
        const char *desc = item->refname;
        char *to_free = NULL;
  
        switch (item->kind) {
        case FILTER_REFS_BRANCHES:
 -              skip_prefix(desc, "refs/heads/", &desc);
 +              prefix_to_skip = "refs/heads/";
 +              skip_prefix(desc, prefix_to_skip, &desc);
                if (!filter->detached && !strcmp(desc, head))
                        current = 1;
                else
                        color = BRANCH_COLOR_LOCAL;
                break;
        case FILTER_REFS_REMOTES:
 -              skip_prefix(desc, "refs/remotes/", &desc);
 +              prefix_to_skip = "refs/remotes/";
 +              skip_prefix(desc, prefix_to_skip, &desc);
                color = BRANCH_COLOR_REMOTE;
 -              prefix = remote_prefix;
 +              prefix_to_show = remote_prefix;
                break;
        case FILTER_REFS_DETACHED_HEAD:
                desc = to_free = get_head_description();
                color = BRANCH_COLOR_CURRENT;
        }
  
 -      strbuf_addf(&name, "%s%s", prefix, desc);
 +      strbuf_addf(&name, "%s%s", prefix_to_show, desc);
        if (filter->verbose) {
                int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
                strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
                            name.buf, branch_get_color(BRANCH_COLOR_RESET));
  
        if (item->symref) {
 -              skip_prefix(item->symref, "refs/remotes/", &desc);
 -              strbuf_addf(&out, " -> %s", desc);
 +              const char *symref = item->symref;
 +              if (prefix_to_skip)
 +                      skip_prefix(symref, prefix_to_skip, &symref);
 +              strbuf_addf(&out, " -> %s", symref);
        }
        else if (filter->verbose)
                /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
@@@ -565,7 -552,8 +565,7 @@@ static void rename_branch(const char *o
        if (recovery)
                warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
  
 -      /* no need to pass logmsg here as HEAD didn't really move */
 -      if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
 +      if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
                die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
  
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
@@@ -595,8 -583,7 +595,7 @@@ static int edit_branch_description(cons
                    branch_name, comment_line_char);
        if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
                strbuf_release(&buf);
-               return error(_("could not write branch description template: %s"),
-                            strerror(errno));
+               return error_errno(_("could not write branch description template"));
        }
        strbuf_reset(&buf);
        if (launch_editor(git_path(edit_description), &buf, NULL)) {
@@@ -632,7 -619,7 +631,7 @@@ int cmd_branch(int argc, const char **a
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
 -              OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
 +              OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
                if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
                    !branch_existed && remote_tracking) {
                        fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
 -                      fprintf(stderr, _("    git branch -d %s\n"), branch->name);
 -                      fprintf(stderr, _("    git branch --set-upstream-to %s\n"), branch->name);
 +                      fprintf(stderr, "    git branch -d %s\n", branch->name);
 +                      fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
                }
  
        } else
diff --combined builtin/fetch.c
index f8455bde7a84e110da182d56f62ac3f89026f55c,4227a40c67ae7fc653e57b049a9fd9e93ca4e3dd..1582ca71847fe4d79366e85cbea86f9f64f55b90
@@@ -37,7 -37,7 +37,7 @@@ static int prune = -1; /* unspecified *
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 -static int max_children = 1;
 +static int max_children = -1;
  static enum transport_family family;
  static const char *depth;
  static const char *upload_pack;
@@@ -607,7 -607,7 +607,7 @@@ static int store_updated_refs(const cha
  
        fp = fopen(filename, "a");
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
  
        if (raw_url)
                url = transport_anonymize_url(raw_url);
@@@ -848,7 -848,7 +848,7 @@@ static int truncate_fetch_head(void
        FILE *fp = fopen_for_writing(filename);
  
        if (!fp)
-               return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+               return error_errno(_("cannot open %s"), filename);
        fclose(fp);
        return 0;
  }
diff --combined builtin/pack-objects.c
index 14dccb5283c47accc6b2be6a02c4ceabc9153a3c,114574742186214e16165548a1dc7f9e386efd76..8f5e358e22b40ff2e162cc7037339ad357622650
@@@ -759,10 -759,6 +759,10 @@@ static off_t write_reused_pack(struct s
        return reuse_packfile_offset - sizeof(struct pack_header);
  }
  
 +static const char no_split_warning[] = N_(
 +"disabling bitmap writing, packs are split due to pack.packSizeLimit"
 +);
 +
  static void write_pack_file(void)
  {
        uint32_t i = 0, j;
                        fixup_pack_header_footer(fd, sha1, pack_tmp_name,
                                                 nr_written, sha1, offset);
                        close(fd);
 -                      write_bitmap_index = 0;
 +                      if (write_bitmap_index) {
 +                              warning(_(no_split_warning));
 +                              write_bitmap_index = 0;
 +                      }
                }
  
                if (!pack_to_stdout) {
                         * to preserve this property.
                         */
                        if (stat(pack_tmp_name, &st) < 0) {
-                               warning("failed to stat %s: %s",
-                                       pack_tmp_name, strerror(errno));
+                               warning_errno("failed to stat %s", pack_tmp_name);
                        } else if (!last_mtime) {
                                last_mtime = st.st_mtime;
                        } else {
                                utb.actime = st.st_atime;
                                utb.modtime = --last_mtime;
                                if (utime(pack_tmp_name, &utb) < 0)
-                                       warning("failed utime() on %s: %s",
-                                               pack_tmp_name, strerror(errno));
+                                       warning_errno("failed utime() on %s", pack_tmp_name);
                        }
  
                        strbuf_addf(&tmpname, "%s-", base_name);
@@@ -1193,7 -1184,7 +1191,7 @@@ static void add_pbase_object(struct tre
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
 -                      add_object_entry(entry.sha1,
 +                      add_object_entry(entry.oid->hash,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
  
 -                      tree = pbase_tree_get(entry.sha1);
 +                      tree = pbase_tree_get(entry.oid->hash);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
diff --combined builtin/rm.c
index be83c4347a9cd6fdd6a76a9575f8e39a5b2bd6c2,fd47d20942269c934d8cf49571afba588a1a622e..8abb0207fa8e4da05ea447ae4e58c8e54c97c35b
@@@ -152,7 -152,7 +152,7 @@@ static int check_local_mod(unsigned cha
  
                if (lstat(ce->name, &st) < 0) {
                        if (errno != ENOENT && errno != ENOTDIR)
-                               warning("'%s': %s", ce->name, strerror(errno));
+                               warning_errno(_("failed to stat '%s'"), ce->name);
                        /* It already vanished from the working tree */
                        continue;
                }
@@@ -314,7 -314,7 +314,7 @@@ int cmd_rm(int argc, const char **argv
                list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
                if (list.entry[list.nr++].is_submodule &&
                    !is_staging_gitmodules_ok())
 -                      die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
 +                      die (_("Please stage your changes to .gitmodules or stash them to proceed"));
        }
  
        if (pathspec.nr) {
diff --combined builtin/worktree.c
index d8e3795dc44a0dc047cac77af92282361227111a,df4f6606c1061de2501f35aceebba558e2bab0f2..331ecf6761d22d256398adeedccdd85edfacd2de
@@@ -21,7 -21,6 +21,7 @@@ static const char * const worktree_usag
  struct add_opts {
        int force;
        int detach;
 +      int checkout;
        const char *new_branch;
        int force_new_branch;
  };
@@@ -110,7 -109,7 +110,7 @@@ static void prune_worktrees(void
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
                if (ret)
-                       error(_("failed to remove: %s"), strerror(errno));
+                       error_errno(_("failed to remove '%s'"), path.buf);
        }
        closedir(dir);
        if (!show_only)
@@@ -285,22 -284,18 +285,22 @@@ static int add_worktree(const char *pat
        if (ret)
                goto done;
  
 -      cp.argv = NULL;
 -      argv_array_clear(&cp.args);
 -      argv_array_pushl(&cp.args, "reset", "--hard", NULL);
 -      cp.env = child_env.argv;
 -      ret = run_command(&cp);
 -      if (!ret) {
 -              is_junk = 0;
 -              free(junk_work_tree);
 -              free(junk_git_dir);
 -              junk_work_tree = NULL;
 -              junk_git_dir = NULL;
 +      if (opts->checkout) {
 +              cp.argv = NULL;
 +              argv_array_clear(&cp.args);
 +              argv_array_pushl(&cp.args, "reset", "--hard", NULL);
 +              cp.env = child_env.argv;
 +              ret = run_command(&cp);
 +              if (ret)
 +                      goto done;
        }
 +
 +      is_junk = 0;
 +      free(junk_work_tree);
 +      free(junk_git_dir);
 +      junk_work_tree = NULL;
 +      junk_git_dir = NULL;
 +
  done:
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
@@@ -325,12 -320,10 +325,12 @@@ static int add(int ac, const char **av
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
                           N_("create or reset a branch")),
                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 +              OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
                OPT_END()
        };
  
        memset(&opts, 0, sizeof(opts));
 +      opts.checkout = 1;
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
                die(_("-b, -B, and --detach are mutually exclusive"));
diff --combined config.c
index 13fb5087772f389f1724a47a077eb3237367fbf5,0bbf640417a772cb81e8d22a739ce4e2b3fa4e21..21ff89cfb056f311e8e3ae258b529b2f207b6a83
+++ b/config.c
@@@ -108,7 -108,7 +108,7 @@@ static int handle_path_include(const ch
  
        expanded = expand_user_path(path);
        if (!expanded)
 -              return error("Could not expand include path '%s'", path);
 +              return error("could not expand include path '%s'", path);
        path = expanded;
  
        /*
@@@ -162,7 -162,7 +162,7 @@@ void git_config_push_parameter(const ch
  {
        struct strbuf env = STRBUF_INIT;
        const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
 -      if (old) {
 +      if (old && *old) {
                strbuf_addstr(&env, old);
                strbuf_addch(&env, ' ');
        }
@@@ -717,9 -717,6 +717,9 @@@ static int git_default_core_config(cons
        if (!strcmp(var, "core.attributesfile"))
                return git_config_pathname(&git_attributes_file, var, value);
  
 +      if (!strcmp(var, "core.hookspath"))
 +              return git_config_pathname(&git_hooks_path, var, value);
 +
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@@ -953,7 -950,7 +953,7 @@@ static int git_default_branch_config(co
                else if (!strcmp(value, "always"))
                        autorebase = AUTOREBASE_ALWAYS;
                else
 -                      return error("Malformed value for %s", var);
 +                      return error("malformed value for %s", var);
                return 0;
        }
  
@@@ -979,7 -976,7 +979,7 @@@ static int git_default_push_config(cons
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
 -                      error("Malformed value for %s: %s", var, value);
 +                      error("malformed value for %s: %s", var, value);
                        return error("Must be one of nothing, matching, simple, "
                                     "upstream or current.");
                }
@@@ -1191,12 -1188,11 +1191,12 @@@ int git_config_system(void
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
  }
  
 -int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 +static int do_git_config_sequence(config_fn_t fn, void *data)
  {
        int ret = 0, found = 0;
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig");
 +      char *repo_config = git_pathdup("config");
  
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
  
        free(xdg_config);
        free(user_config);
 +      free(repo_config);
        return ret == 0 ? found : ret;
  }
  
@@@ -1240,6 -1235,8 +1240,6 @@@ int git_config_with_options(config_fn_
                            struct git_config_source *config_source,
                            int respect_includes)
  {
 -      char *repo_config = NULL;
 -      int ret;
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
  
        if (respect_includes) {
        else if (config_source && config_source->blob)
                return git_config_from_blob_ref(fn, config_source->blob, data);
  
 -      repo_config = git_pathdup("config");
 -      ret = git_config_early(fn, data, repo_config);
 -      if (repo_config)
 -              free(repo_config);
 -      return ret;
 +      return do_git_config_sequence(fn, data);
  }
  
  static void git_config_raw(config_fn_t fn, void *data)
@@@ -1312,11 -1313,14 +1312,11 @@@ static struct config_set_element *confi
        struct config_set_element k;
        struct config_set_element *found_entry;
        char *normalized_key;
 -      int ret;
        /*
         * `key` may come from the user, so normalize it before using it
         * for querying entries from the hashmap.
         */
 -      ret = git_config_parse_key(key, &normalized_key, NULL);
 -
 -      if (ret)
 +      if (git_config_parse_key(key, &normalized_key, NULL))
                return NULL;
  
        hashmap_entry_init(&k, strhash(normalized_key));
@@@ -2012,7 -2016,7 +2012,7 @@@ int git_config_set_multivar_in_file_gen
        lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
-               error("could not lock config file %s: %s", config_filename, strerror(errno));
+               error_errno("could not lock config file %s", config_filename);
                free(store.key);
                ret = CONFIG_NO_LOCK;
                goto out_free;
                free(store.key);
  
                if ( ENOENT != errno ) {
-                       error("opening %s: %s", config_filename,
-                             strerror(errno));
+                       error_errno("opening %s", config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
-                       error("unable to mmap '%s': %s",
-                             config_filename, strerror(errno));
+                       error_errno("unable to mmap '%s'", config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
                in_fd = -1;
  
                if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error("chmod on %s failed: %s",
-                             get_lock_file_path(lock), strerror(errno));
+                       error_errno("chmod on %s failed", get_lock_file_path(lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
        }
  
        if (commit_lock_file(lock) < 0) {
-               error("could not write config file %s: %s", config_filename,
-                     strerror(errno));
+               error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@@ -2217,13 -2217,9 +2213,13 @@@ void git_config_set_multivar_in_file(co
                                     const char *key, const char *value,
                                     const char *value_regex, int multi_replace)
  {
 -      if (git_config_set_multivar_in_file_gently(config_filename, key, value,
 -                                                 value_regex, multi_replace) < 0)
 -              die(_("Could not set '%s' to '%s'"), key, value);
 +      if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
 +                                                  value_regex, multi_replace))
 +              return;
 +      if (value)
 +              die(_("could not set '%s' to '%s'"), key, value);
 +      else
 +              die(_("could not unset '%s'"), key);
  }
  
  int git_config_set_multivar_gently(const char *key, const char *value,
@@@ -2330,8 -2326,8 +2326,8 @@@ int git_config_rename_section_in_file(c
        fstat(fileno(config_file), &st);
  
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-               ret = error("chmod on %s failed: %s",
-                           get_lock_file_path(lock), strerror(errno));
+               ret = error_errno("chmod on %s failed",
+                                 get_lock_file_path(lock));
                goto out;
        }
  
        fclose(config_file);
  unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not write config file %s: %s",
-                           config_filename, strerror(errno));
+               ret = error_errno("could not write config file %s",
+                                 config_filename);
  out:
        free(filename_buf);
        return ret;
@@@ -2404,7 -2400,7 +2400,7 @@@ int git_config_rename_section(const cha
  #undef config_error_nonbool
  int config_error_nonbool(const char *var)
  {
 -      return error("Missing value for '%s'", var);
 +      return error("missing value for '%s'", var);
  }
  
  int parse_config_key(const char *var,
index 291c0fd5e935b5abedc629697d44e5a9f57727bd,94d18f839cd356e0911ae6e9452536a34cb16400..1f14d56e98834e73dce91f3a20c3e3795ba6fb37
@@@ -126,17 -126,8 +126,17 @@@ static void serve_one_client(FILE *in, 
                        fprintf(out, "password=%s\n", e->item.password);
                }
        }
 -      else if (!strcmp(action.buf, "exit"))
 +      else if (!strcmp(action.buf, "exit")) {
 +              /*
 +               * It's important that we clean up our socket first, and then
 +               * signal the client only once we have finished the cleanup.
 +               * Calling exit() directly does this, because we clean up in
 +               * our atexit() handler, and then signal the client when our
 +               * process actually ends, which closes the socket and gives
 +               * them EOF.
 +               */
                exit(0);
 +      }
        else if (!strcmp(action.buf, "erase"))
                remove_credential(&c);
        else if (!strcmp(action.buf, "store")) {
@@@ -179,12 -170,12 +179,12 @@@ static int serve_cache_loop(int fd
  
                client = accept(fd, NULL, NULL);
                if (client < 0) {
-                       warning("accept failed: %s", strerror(errno));
+                       warning_errno("accept failed");
                        return 1;
                }
                client2 = dup(client);
                if (client2 < 0) {
-                       warning("dup failed: %s", strerror(errno));
+                       warning_errno("dup failed");
                        close(client);
                        return 1;
                }
diff --combined git-compat-util.h
index 1f8b5f3b1f1ac17716681fee2d72b9c124a9b99c,76efb727daaa850dad6117dc56b872d66d8b85b4..49d4029b8dddcb06dc6bea3d5f47c020785e3ddf
@@@ -279,6 -279,9 +279,6 @@@ extern char *gitdirname(char *)
  #endif
  #include <openssl/ssl.h>
  #include <openssl/err.h>
 -#ifdef NO_HMAC_CTX_CLEANUP
 -#define HMAC_CTX_cleanup HMAC_cleanup
 -#endif
  #endif
  
  /* On most systems <netdb.h> would have given us this, but
@@@ -409,7 -412,9 +409,9 @@@ extern NORETURN void usagef(const char 
  extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
  extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
  extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+ extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
  extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+ extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
  
  #ifndef NO_OPENSSL
  #ifdef APPLE_COMMON_CRYPTO
diff --combined gpg-interface.c
index 22599382365eb564cdfc22b434cb60796e06b82f,20612fe5017837575e80005844486327e9e3e699..c4b1e8c78d396194753d8a7287678bc476f858d6
@@@ -219,11 -219,9 +219,9 @@@ int verify_signed_buffer(const char *pa
        args_gpg[0] = gpg_program;
        fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
        if (fd < 0)
-               return error(_("could not create temporary file '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("could not create temporary file '%s'"), path);
        if (write_in_full(fd, signature, signature_size) < 0)
-               return error(_("failed writing detached signature to '%s': %s"),
-                            path, strerror(errno));
+               return error_errno(_("failed writing detached signature to '%s'"), path);
        close(fd);
  
        gpg.argv = args_gpg;
                return error(_("could not run gpg."));
        }
  
 +      sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(gpg.in, payload, payload_size);
        close(gpg.in);
  
        close(gpg.out);
  
        ret = finish_command(&gpg);
 +      sigchain_pop(SIGPIPE);
  
        unlink_or_warn(path);
  
diff --combined http.c
index 6fe74d5eeafa0566cc7f19c8024ed0c82b55db77,fa39b879cecbe3402075600ce9edfbee08eee986..df6dd01594a0c7bacbb9beacec683072de38a5b1
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -114,7 -114,6 +114,7 @@@ static unsigned long http_auth_methods 
  
  static struct curl_slist *pragma_header;
  static struct curl_slist *no_pragma_header;
 +static struct curl_slist *extra_http_headers;
  
  static struct active_request_slot *active_queue_head;
  
@@@ -294,7 -293,7 +294,7 @@@ static int http_options(const char *var
                return git_config_string(&http_proxy_authmethod, var, value);
  
        if (!strcmp("http.cookiefile", var))
 -              return git_config_string(&curl_cookie_file, var, value);
 +              return git_config_pathname(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
                curl_save_cookies = git_config_bool(var, value);
                return 0;
  #endif
        }
  
 +      if (!strcmp("http.extraheader", var)) {
 +              if (!value) {
 +                      return config_error_nonbool(var);
 +              } else if (!*value) {
 +                      curl_slist_free_all(extra_http_headers);
 +                      extra_http_headers = NULL;
 +              } else {
 +                      extra_http_headers =
 +                              curl_slist_append(extra_http_headers, value);
 +              }
 +              return 0;
 +      }
 +
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
  }
@@@ -460,8 -446,7 +460,7 @@@ static int sockopt_callback(void *clien
  
        rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);
        if (rc < 0)
-               warning("unable to set SO_KEEPALIVE on socket %s",
-                       strerror(errno));
+               warning_errno("unable to set SO_KEEPALIVE on socket");
  
        return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
  }
@@@ -619,10 -604,7 +618,10 @@@ static CURL *get_curl_handle(void
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
  #if LIBCURL_VERSION_NUM >= 0x071800
 -              if (starts_with(curl_http_proxy, "socks5"))
 +              if (starts_with(curl_http_proxy, "socks5h"))
 +                      curl_easy_setopt(result,
 +                              CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
 +              else if (starts_with(curl_http_proxy, "socks5"))
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
                else if (starts_with(curl_http_proxy, "socks4a"))
@@@ -692,10 -674,8 +691,10 @@@ void http_init(struct remote *remote, c
        if (remote)
                var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
  
 -      pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
 -      no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 +      pragma_header = curl_slist_append(http_copy_default_headers(),
 +              "Pragma: no-cache");
 +      no_pragma_header = curl_slist_append(http_copy_default_headers(),
 +              "Pragma:");
  
  #ifdef USE_CURL_MULTI
        {
@@@ -781,9 -761,6 +780,9 @@@ void http_cleanup(void
  #endif
        curl_global_cleanup();
  
 +      curl_slist_free_all(extra_http_headers);
 +      extra_http_headers = NULL;
 +
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
  
@@@ -1182,16 -1159,6 +1181,16 @@@ int run_one_slot(struct active_request_
        return handle_curl_result(results);
  }
  
 +struct curl_slist *http_copy_default_headers(void)
 +{
 +      struct curl_slist *headers = NULL, *h;
 +
 +      for (h = extra_http_headers; h; h = h->next)
 +              headers = curl_slist_append(headers, h->data);
 +
 +      return headers;
 +}
 +
  static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
  {
        char *ptr;
@@@ -1409,7 -1376,7 +1408,7 @@@ static int http_request(const char *url
  {
        struct active_request_slot *slot;
        struct slot_results results;
 -      struct curl_slist *headers = NULL;
 +      struct curl_slist *headers = http_copy_default_headers();
        struct strbuf buf = STRBUF_INIT;
        const char *accept_language;
        int ret;
@@@ -1923,8 -1890,7 +1922,7 @@@ struct http_object_request *new_http_ob
        }
  
        if (freq->localfile < 0) {
-               error("Couldn't create temporary file %s: %s",
-                     freq->tmpfile, strerror(errno));
+               error_errno("Couldn't create temporary file %s", freq->tmpfile);
                goto abort;
        }
  
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
-                               error("Couldn't truncate temporary file %s: %s",
-                                         freq->tmpfile, strerror(errno));
+                               error_errno("Couldn't truncate temporary file %s",
+                                           freq->tmpfile);
                                goto abort;
                        }
                }
diff --combined ident.c
index 4fd82d104365c2e2c1ab7e1597e7e246c8eded24,337a4210a57df62e6e2e9c7fc35a9f3116536d86..139c5289d03b7594af2a07e6e0364bb285ae0348
+++ b/ident.c
@@@ -75,14 -75,12 +75,12 @@@ static int add_mailname_host(struct str
        mailname = fopen("/etc/mailname", "r");
        if (!mailname) {
                if (errno != ENOENT)
-                       warning("cannot open /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot open /etc/mailname");
                return -1;
        }
        if (strbuf_getline(&mailnamebuf, mailname) == EOF) {
                if (ferror(mailname))
-                       warning("cannot read /etc/mailname: %s",
-                               strerror(errno));
+                       warning_errno("cannot read /etc/mailname");
                strbuf_release(&mailnamebuf);
                fclose(mailname);
                return -1;
@@@ -125,7 -123,7 +123,7 @@@ static void add_domainname(struct strbu
        char buf[1024];
  
        if (gethostname(buf, sizeof(buf))) {
-               warning("cannot get host name: %s", strerror(errno));
+               warning_errno("cannot get host name");
                strbuf_addstr(out, "(none)");
                *is_bogus = 1;
                return;
@@@ -351,17 -349,15 +349,17 @@@ const char *fmt_ident(const char *name
        if (want_name) {
                int using_default = 0;
                if (!name) {
 +                      if (strict && ident_use_config_only
 +                          && !(ident_config_given & IDENT_NAME_GIVEN)) {
 +                              fputs(env_hint, stderr);
 +                              die("no name was given and auto-detection is disabled");
 +                      }
                        name = ident_default_name();
                        using_default = 1;
                        if (strict && default_name_is_bogus) {
                                fputs(env_hint, stderr);
                                die("unable to auto-detect name (got '%s')", name);
                        }
 -                      if (strict && ident_use_config_only
 -                          && !(ident_config_given & IDENT_NAME_GIVEN))
 -                              die("user.useConfigOnly set but no name given");
                }
                if (!*name) {
                        struct passwd *pw;
        }
  
        if (!email) {
 +              if (strict && ident_use_config_only
 +                  && !(ident_config_given & IDENT_MAIL_GIVEN)) {
 +                      fputs(env_hint, stderr);
 +                      die("no email was given and auto-detection is disabled");
 +              }
                email = ident_default_email();
                if (strict && default_email_is_bogus) {
                        fputs(env_hint, stderr);
                        die("unable to auto-detect email address (got '%s')", email);
                }
 -              if (strict && ident_use_config_only
 -                  && !(ident_config_given & IDENT_MAIL_GIVEN))
 -                      die("user.useConfigOnly set but no mail given");
        }
  
        strbuf_reset(&ident);
diff --combined mailmap.c
index 972623709fdac5503328f7e252d5334dfcb461c6,da4e49aac8966039a2ac7c989480c3efb2d52e24..b5c521fdea8b0bfc577795b172010dfc2b8bf8ff
+++ b/mailmap.c
@@@ -189,8 -189,7 +189,7 @@@ static int read_mailmap_file(struct str
        if (!f) {
                if (errno == ENOENT)
                        return 0;
-               return error("unable to open mailmap at %s: %s",
-                            filename, strerror(errno));
+               return error_errno("unable to open mailmap at %s", filename);
        }
  
        while (fgets(buffer, sizeof(buffer), f) != NULL)
@@@ -250,8 -249,7 +249,8 @@@ int read_mailmap(struct string_list *ma
                git_mailmap_blob = "HEAD:.mailmap";
  
        err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
 -      err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
 +      if (startup_info->have_repository)
 +              err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
        err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
        return err;
  }
diff --combined rerere.c
index c8b9f407872f431399a4c5c321742c32cded5711,9e9c66ca02627bcb249e9c90dfd68481d412975e..1810c040daba6398580bd9a40179b365887a91a6
+++ b/rerere.c
@@@ -8,7 -8,6 +8,7 @@@
  #include "ll-merge.h"
  #include "attr.h"
  #include "pathspec.h"
 +#include "sha1-lookup.h"
  
  #define RESOLVED 0
  #define PUNTED 1
@@@ -21,29 -20,6 +21,29 @@@ static int rerere_enabled = -1
  /* automatically update cleanly resolved paths to the index */
  static int rerere_autoupdate;
  
 +static int rerere_dir_nr;
 +static int rerere_dir_alloc;
 +
 +#define RR_HAS_POSTIMAGE 1
 +#define RR_HAS_PREIMAGE 2
 +static struct rerere_dir {
 +      unsigned char sha1[20];
 +      int status_alloc, status_nr;
 +      unsigned char *status;
 +} **rerere_dir;
 +
 +static void free_rerere_dirs(void)
 +{
 +      int i;
 +      for (i = 0; i < rerere_dir_nr; i++) {
 +              free(rerere_dir[i]->status);
 +              free(rerere_dir[i]);
 +      }
 +      free(rerere_dir);
 +      rerere_dir_nr = rerere_dir_alloc = 0;
 +      rerere_dir = NULL;
 +}
 +
  static void free_rerere_id(struct string_list_item *item)
  {
        free(item->util);
  
  static const char *rerere_id_hex(const struct rerere_id *id)
  {
 -      return id->hex;
 +      return sha1_to_hex(id->collection->sha1);
 +}
 +
 +static void fit_variant(struct rerere_dir *rr_dir, int variant)
 +{
 +      variant++;
 +      ALLOC_GROW(rr_dir->status, variant, rr_dir->status_alloc);
 +      if (rr_dir->status_nr < variant) {
 +              memset(rr_dir->status + rr_dir->status_nr,
 +                     '\0', variant - rr_dir->status_nr);
 +              rr_dir->status_nr = variant;
 +      }
 +}
 +
 +static void assign_variant(struct rerere_id *id)
 +{
 +      int variant;
 +      struct rerere_dir *rr_dir = id->collection;
 +
 +      variant = id->variant;
 +      if (variant < 0) {
 +              for (variant = 0; variant < rr_dir->status_nr; variant++)
 +                      if (!rr_dir->status[variant])
 +                              break;
 +      }
 +      fit_variant(rr_dir, variant);
 +      id->variant = variant;
  }
  
  const char *rerere_path(const struct rerere_id *id, const char *file)
        if (!file)
                return git_path("rr-cache/%s", rerere_id_hex(id));
  
 -      return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
 +      if (id->variant <= 0)
 +              return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
 +
 +      return git_path("rr-cache/%s/%s.%d",
 +                      rerere_id_hex(id), file, id->variant);
 +}
 +
 +static int is_rr_file(const char *name, const char *filename, int *variant)
 +{
 +      const char *suffix;
 +      char *ep;
 +
 +      if (!strcmp(name, filename)) {
 +              *variant = 0;
 +              return 1;
 +      }
 +      if (!skip_prefix(name, filename, &suffix) || *suffix != '.')
 +              return 0;
 +
 +      errno = 0;
 +      *variant = strtol(suffix + 1, &ep, 10);
 +      if (errno || *ep)
 +              return 0;
 +      return 1;
 +}
 +
 +static void scan_rerere_dir(struct rerere_dir *rr_dir)
 +{
 +      struct dirent *de;
 +      DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->sha1)));
 +
 +      if (!dir)
 +              return;
 +      while ((de = readdir(dir)) != NULL) {
 +              int variant;
 +
 +              if (is_rr_file(de->d_name, "postimage", &variant)) {
 +                      fit_variant(rr_dir, variant);
 +                      rr_dir->status[variant] |= RR_HAS_POSTIMAGE;
 +              } else if (is_rr_file(de->d_name, "preimage", &variant)) {
 +                      fit_variant(rr_dir, variant);
 +                      rr_dir->status[variant] |= RR_HAS_PREIMAGE;
 +              }
 +      }
 +      closedir(dir);
 +}
 +
 +static const unsigned char *rerere_dir_sha1(size_t i, void *table)
 +{
 +      struct rerere_dir **rr_dir = table;
 +      return rr_dir[i]->sha1;
 +}
 +
 +static struct rerere_dir *find_rerere_dir(const char *hex)
 +{
 +      unsigned char sha1[20];
 +      struct rerere_dir *rr_dir;
 +      int pos;
 +
 +      if (get_sha1_hex(hex, sha1))
 +              return NULL; /* BUG */
 +      pos = sha1_pos(sha1, rerere_dir, rerere_dir_nr, rerere_dir_sha1);
 +      if (pos < 0) {
 +              rr_dir = xmalloc(sizeof(*rr_dir));
 +              hashcpy(rr_dir->sha1, sha1);
 +              rr_dir->status = NULL;
 +              rr_dir->status_nr = 0;
 +              rr_dir->status_alloc = 0;
 +              pos = -1 - pos;
 +
 +              /* Make sure the array is big enough ... */
 +              ALLOC_GROW(rerere_dir, rerere_dir_nr + 1, rerere_dir_alloc);
 +              /* ... and add it in. */
 +              rerere_dir_nr++;
 +              memmove(rerere_dir + pos + 1, rerere_dir + pos,
 +                      (rerere_dir_nr - pos - 1) * sizeof(*rerere_dir));
 +              rerere_dir[pos] = rr_dir;
 +              scan_rerere_dir(rr_dir);
 +      }
 +      return rerere_dir[pos];
  }
  
  static int has_rerere_resolution(const struct rerere_id *id)
  {
 -      struct stat st;
 +      const int both = RR_HAS_POSTIMAGE|RR_HAS_PREIMAGE;
 +      int variant = id->variant;
  
 -      return !stat(rerere_path(id, "postimage"), &st);
 +      if (variant < 0)
 +              return 0;
 +      return ((id->collection->status[variant] & both) == both);
  }
  
  static struct rerere_id *new_rerere_id_hex(char *hex)
  {
        struct rerere_id *id = xmalloc(sizeof(*id));
 -      xsnprintf(id->hex, sizeof(id->hex), "%s", hex);
 +      id->collection = find_rerere_dir(hex);
 +      id->variant = -1; /* not known yet */
        return id;
  }
  
@@@ -208,26 -75,16 +208,26 @@@ static void read_rr(struct string_list 
                char *path;
                unsigned char sha1[20];
                struct rerere_id *id;
 +              int variant;
  
                /* There has to be the hash, tab, path and then NUL */
                if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
                        die("corrupt MERGE_RR");
  
 -              if (buf.buf[40] != '\t')
 +              if (buf.buf[40] != '.') {
 +                      variant = 0;
 +                      path = buf.buf + 40;
 +              } else {
 +                      errno = 0;
 +                      variant = strtol(buf.buf + 41, &path, 10);
 +                      if (errno)
 +                              die("corrupt MERGE_RR");
 +              }
 +              if (*(path++) != '\t')
                        die("corrupt MERGE_RR");
                buf.buf[40] = '\0';
 -              path = buf.buf + 41;
                id = new_rerere_id_hex(buf.buf);
 +              id->variant = variant;
                string_list_insert(rr, path)->util = id;
        }
        strbuf_release(&buf);
@@@ -248,16 -105,9 +248,16 @@@ static int write_rr(struct string_list 
                id = rr->items[i].util;
                if (!id)
                        continue;
 -              strbuf_addf(&buf, "%s\t%s%c",
 -                          rerere_id_hex(id),
 -                          rr->items[i].string, 0);
 +              assert(id->variant >= 0);
 +              if (0 < id->variant)
 +                      strbuf_addf(&buf, "%s.%d\t%s%c",
 +                                  rerere_id_hex(id), id->variant,
 +                                  rr->items[i].string, 0);
 +              else
 +                      strbuf_addf(&buf, "%s\t%s%c",
 +                                  rerere_id_hex(id),
 +                                  rr->items[i].string, 0);
 +
                if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
                        die("unable to write rerere record");
  
@@@ -501,8 -351,7 +501,7 @@@ static int handle_file(const char *path
                error("There were errors while writing %s (%s)",
                      path, strerror(io.io.wrerror));
        if (io.io.output && fclose(io.io.output))
-               io.io.wrerror = error("Failed to flush %s: %s",
-                                     path, strerror(errno));
+               io.io.wrerror = error_errno("Failed to flush %s", path);
  
        if (hunk_no < 0) {
                if (output)
        return hunk_no;
  }
  
 -/*
 - * Subclass of rerere_io that reads from an in-core buffer that is a
 - * strbuf
 - */
 -struct rerere_io_mem {
 -      struct rerere_io io;
 -      struct strbuf input;
 -};
 -
 -/*
 - * ... and its getline() method implementation
 - */
 -static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
 -{
 -      struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
 -      char *ep;
 -      size_t len;
 -
 -      strbuf_release(sb);
 -      if (!io->input.len)
 -              return -1;
 -      ep = memchr(io->input.buf, '\n', io->input.len);
 -      if (!ep)
 -              ep = io->input.buf + io->input.len;
 -      else if (*ep == '\n')
 -              ep++;
 -      len = ep - io->input.buf;
 -      strbuf_add(sb, io->input.buf, len);
 -      strbuf_remove(&io->input, 0, len);
 -      return 0;
 -}
 -
 -static int handle_cache(const char *path, unsigned char *sha1, const char *output)
 -{
 -      mmfile_t mmfile[3] = {{NULL}};
 -      mmbuffer_t result = {NULL, 0};
 -      const struct cache_entry *ce;
 -      int pos, len, i, hunk_no;
 -      struct rerere_io_mem io;
 -      int marker_size = ll_merge_marker_size(path);
 -
 -      /*
 -       * Reproduce the conflicted merge in-core
 -       */
 -      len = strlen(path);
 -      pos = cache_name_pos(path, len);
 -      if (0 <= pos)
 -              return -1;
 -      pos = -pos - 1;
 -
 -      while (pos < active_nr) {
 -              enum object_type type;
 -              unsigned long size;
 -
 -              ce = active_cache[pos++];
 -              if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
 -                      break;
 -              i = ce_stage(ce) - 1;
 -              if (!mmfile[i].ptr) {
 -                      mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
 -                      mmfile[i].size = size;
 -              }
 -      }
 -      for (i = 0; i < 3; i++)
 -              if (!mmfile[i].ptr && !mmfile[i].size)
 -                      mmfile[i].ptr = xstrdup("");
 -
 -      /*
 -       * NEEDSWORK: handle conflicts from merges with
 -       * merge.renormalize set, too
 -       */
 -      ll_merge(&result, path, &mmfile[0], NULL,
 -               &mmfile[1], "ours",
 -               &mmfile[2], "theirs", NULL);
 -      for (i = 0; i < 3; i++)
 -              free(mmfile[i].ptr);
 -
 -      memset(&io, 0, sizeof(io));
 -      io.io.getline = rerere_mem_getline;
 -      if (output)
 -              io.io.output = fopen(output, "w");
 -      else
 -              io.io.output = NULL;
 -      strbuf_init(&io.input, 0);
 -      strbuf_attach(&io.input, result.ptr, result.size, result.size);
 -
 -      /*
 -       * Grab the conflict ID and optionally write the original
 -       * contents with conflict markers out.
 -       */
 -      hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
 -      strbuf_release(&io.input);
 -      if (io.io.output)
 -              fclose(io.io.output);
 -      return hunk_no;
 -}
 -
  /*
   * Look at a cache entry at "i" and see if it is not conflicting,
   * conflicting and we are willing to handle, or conflicting and
@@@ -621,33 -567,6 +620,33 @@@ int rerere_remaining(struct string_lis
        return 0;
  }
  
 +/*
 + * Try using the given conflict resolution "ID" to see
 + * if that recorded conflict resolves cleanly what we
 + * got in the "cur".
 + */
 +static int try_merge(const struct rerere_id *id, const char *path,
 +                   mmfile_t *cur, mmbuffer_t *result)
 +{
 +      int ret;
 +      mmfile_t base = {NULL, 0}, other = {NULL, 0};
 +
 +      if (read_mmfile(&base, rerere_path(id, "preimage")) ||
 +          read_mmfile(&other, rerere_path(id, "postimage")))
 +              ret = 1;
 +      else
 +              /*
 +               * A three-way merge. Note that this honors user-customizable
 +               * low-level merge driver settings.
 +               */
 +              ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
 +
 +      free(base.ptr);
 +      free(other.ptr);
 +
 +      return ret;
 +}
 +
  /*
   * Find the conflict identified by "id"; the change between its
   * "preimage" (i.e. a previous contents with conflict markers) and its
@@@ -662,20 -581,30 +661,20 @@@ static int merge(const struct rerere_i
  {
        FILE *f;
        int ret;
 -      mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
 +      mmfile_t cur = {NULL, 0};
        mmbuffer_t result = {NULL, 0};
  
        /*
         * Normalize the conflicts in path and write it out to
         * "thisimage" temporary file.
         */
 -      if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
 -              ret = 1;
 -              goto out;
 -      }
 -
 -      if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
 -          read_mmfile(&base, rerere_path(id, "preimage")) ||
 -          read_mmfile(&other, rerere_path(id, "postimage"))) {
 +      if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
 +          read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                ret = 1;
                goto out;
        }
  
 -      /*
 -       * A three-way merge. Note that this honors user-customizable
 -       * low-level merge driver settings.
 -       */
 -      ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
 +      ret = try_merge(id, path, &cur, &result);
        if (ret)
                goto out;
  
         * Mark that "postimage" was used to help gc.
         */
        if (utime(rerere_path(id, "postimage"), NULL) < 0)
-               warning("failed utime() on %s: %s",
-                       rerere_path(id, "postimage"),
-                       strerror(errno));
+               warning_errno("failed utime() on %s",
+                             rerere_path(id, "postimage"));
  
        /* Update "path" with the resolution */
        f = fopen(path, "w");
        if (!f)
-               return error("Could not open %s: %s", path,
-                            strerror(errno));
+               return error_errno("Could not open %s", path);
        if (fwrite(result.ptr, result.size, 1, f) != 1)
-               error("Could not write %s: %s", path, strerror(errno));
+               error_errno("Could not write %s", path);
        if (fclose(f))
-               return error("Writing %s failed: %s", path,
-                            strerror(errno));
+               return error_errno("Writing %s failed", path);
  
  out:
        free(cur.ptr);
 -      free(base.ptr);
 -      free(other.ptr);
        free(result.ptr);
  
        return ret;
@@@ -729,13 -657,6 +725,13 @@@ static void update_paths(struct string_
                rollback_lock_file(&index_lock);
  }
  
 +static void remove_variant(struct rerere_id *id)
 +{
 +      unlink_or_warn(rerere_path(id, "postimage"));
 +      unlink_or_warn(rerere_path(id, "preimage"));
 +      id->collection->status[id->variant] = 0;
 +}
 +
  /*
   * The path indicated by rr_item may still have conflict for which we
   * have a recorded resolution, in which case replay it and optionally
@@@ -747,47 -668,12 +743,47 @@@ static void do_rerere_one_path(struct s
                               struct string_list *update)
  {
        const char *path = rr_item->string;
 -      const struct rerere_id *id = rr_item->util;
 +      struct rerere_id *id = rr_item->util;
 +      struct rerere_dir *rr_dir = id->collection;
 +      int variant;
 +
 +      variant = id->variant;
 +
 +      /* Has the user resolved it already? */
 +      if (variant >= 0) {
 +              if (!handle_file(path, NULL, NULL)) {
 +                      copy_file(rerere_path(id, "postimage"), path, 0666);
 +                      id->collection->status[variant] |= RR_HAS_POSTIMAGE;
 +                      fprintf(stderr, "Recorded resolution for '%s'.\n", path);
 +                      free_rerere_id(rr_item);
 +                      rr_item->util = NULL;
 +                      return;
 +              }
 +              /*
 +               * There may be other variants that can cleanly
 +               * replay.  Try them and update the variant number for
 +               * this one.
 +               */
 +      }
 +
 +      /* Does any existing resolution apply cleanly? */
 +      for (variant = 0; variant < rr_dir->status_nr; variant++) {
 +              const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE;
 +              struct rerere_id vid = *id;
 +
 +              if ((rr_dir->status[variant] & both) != both)
 +                      continue;
  
 -      /* Is there a recorded resolution we could attempt to apply? */
 -      if (has_rerere_resolution(id)) {
 -              if (merge(id, path))
 -                      return; /* failed to replay */
 +              vid.variant = variant;
 +              if (merge(&vid, path))
 +                      continue; /* failed to replay */
 +
 +              /*
 +               * If there already is a different variant that applies
 +               * cleanly, there is no point maintaining our own variant.
 +               */
 +              if (0 <= id->variant && id->variant != variant)
 +                      remove_variant(id);
  
                if (rerere_autoupdate)
                        string_list_insert(update, path);
                        fprintf(stderr,
                                "Resolved '%s' using previous resolution.\n",
                                path);
 -      } else if (!handle_file(path, NULL, NULL)) {
 -              /* The user has resolved it. */
 -              copy_file(rerere_path(id, "postimage"), path, 0666);
 -              fprintf(stderr, "Recorded resolution for '%s'.\n", path);
 -      } else {
 +              free_rerere_id(rr_item);
 +              rr_item->util = NULL;
                return;
        }
 -      free_rerere_id(rr_item);
 -      rr_item->util = NULL;
 +
 +      /* None of the existing one applies; we need a new variant */
 +      assign_variant(id);
 +
 +      variant = id->variant;
 +      handle_file(path, NULL, rerere_path(id, "preimage"));
 +      if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
 +              const char *path = rerere_path(id, "postimage");
 +              if (unlink(path))
 +                      die_errno("cannot unlink stray '%s'", path);
 +              id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
 +      }
 +      id->collection->status[variant] |= RR_HAS_PREIMAGE;
 +      fprintf(stderr, "Recorded preimage for '%s'\n", path);
  }
  
  static int do_plain_rerere(struct string_list *rr, int fd)
                id = new_rerere_id(sha1);
                string_list_insert(rr, path)->util = id;
  
 -              /*
 -               * If the directory does not exist, create
 -               * it.  mkdir_in_gitdir() will fail with
 -               * EEXIST if there already is one.
 -               *
 -               * NEEDSWORK: make sure "gc" does not remove
 -               * preimage without removing the directory.
 -               */
 -              if (mkdir_in_gitdir(rerere_path(id, NULL)))
 -                      continue;
 -
 -              /*
 -               * We are the first to encounter this
 -               * conflict.  Ask handle_file() to write the
 -               * normalized contents to the "preimage" file.
 -               */
 -              handle_file(path, NULL, rerere_path(id, "preimage"));
 -              fprintf(stderr, "Recorded preimage for '%s'\n", path);
 +              /* Ensure that the directory exists. */
 +              mkdir_in_gitdir(rerere_path(id, NULL));
        }
  
        for (i = 0; i < rr->nr; i++)
@@@ -915,111 -808,12 +911,111 @@@ int setup_rerere(struct string_list *me
  int rerere(int flags)
  {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
 -      int fd;
 +      int fd, status;
  
        fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
 -      return do_plain_rerere(&merge_rr, fd);
 +      status = do_plain_rerere(&merge_rr, fd);
 +      free_rerere_dirs();
 +      return status;
 +}
 +
 +/*
 + * Subclass of rerere_io that reads from an in-core buffer that is a
 + * strbuf
 + */
 +struct rerere_io_mem {
 +      struct rerere_io io;
 +      struct strbuf input;
 +};
 +
 +/*
 + * ... and its getline() method implementation
 + */
 +static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
 +{
 +      struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
 +      char *ep;
 +      size_t len;
 +
 +      strbuf_release(sb);
 +      if (!io->input.len)
 +              return -1;
 +      ep = memchr(io->input.buf, '\n', io->input.len);
 +      if (!ep)
 +              ep = io->input.buf + io->input.len;
 +      else if (*ep == '\n')
 +              ep++;
 +      len = ep - io->input.buf;
 +      strbuf_add(sb, io->input.buf, len);
 +      strbuf_remove(&io->input, 0, len);
 +      return 0;
 +}
 +
 +static int handle_cache(const char *path, unsigned char *sha1, const char *output)
 +{
 +      mmfile_t mmfile[3] = {{NULL}};
 +      mmbuffer_t result = {NULL, 0};
 +      const struct cache_entry *ce;
 +      int pos, len, i, hunk_no;
 +      struct rerere_io_mem io;
 +      int marker_size = ll_merge_marker_size(path);
 +
 +      /*
 +       * Reproduce the conflicted merge in-core
 +       */
 +      len = strlen(path);
 +      pos = cache_name_pos(path, len);
 +      if (0 <= pos)
 +              return -1;
 +      pos = -pos - 1;
 +
 +      while (pos < active_nr) {
 +              enum object_type type;
 +              unsigned long size;
 +
 +              ce = active_cache[pos++];
 +              if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
 +                      break;
 +              i = ce_stage(ce) - 1;
 +              if (!mmfile[i].ptr) {
 +                      mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
 +                      mmfile[i].size = size;
 +              }
 +      }
 +      for (i = 0; i < 3; i++)
 +              if (!mmfile[i].ptr && !mmfile[i].size)
 +                      mmfile[i].ptr = xstrdup("");
 +
 +      /*
 +       * NEEDSWORK: handle conflicts from merges with
 +       * merge.renormalize set, too?
 +       */
 +      ll_merge(&result, path, &mmfile[0], NULL,
 +               &mmfile[1], "ours",
 +               &mmfile[2], "theirs", NULL);
 +      for (i = 0; i < 3; i++)
 +              free(mmfile[i].ptr);
 +
 +      memset(&io, 0, sizeof(io));
 +      io.io.getline = rerere_mem_getline;
 +      if (output)
 +              io.io.output = fopen(output, "w");
 +      else
 +              io.io.output = NULL;
 +      strbuf_init(&io.input, 0);
 +      strbuf_attach(&io.input, result.ptr, result.size, result.size);
 +
 +      /*
 +       * Grab the conflict ID and optionally write the original
 +       * contents with conflict markers out.
 +       */
 +      hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
 +      strbuf_release(&io.input);
 +      if (io.io.output)
 +              fclose(io.io.output);
 +      return hunk_no;
  }
  
  static int rerere_forget_one_path(const char *path, struct string_list *rr)
  
        /* Nuke the recorded resolution for the conflict */
        id = new_rerere_id(sha1);
 +
 +      for (id->variant = 0;
 +           id->variant < id->collection->status_nr;
 +           id->variant++) {
 +              mmfile_t cur = { NULL, 0 };
 +              mmbuffer_t result = {NULL, 0};
 +              int cleanly_resolved;
 +
 +              if (!has_rerere_resolution(id))
 +                      continue;
 +
 +              handle_cache(path, sha1, rerere_path(id, "thisimage"));
 +              if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
 +                      free(cur.ptr);
 +                      return error("Failed to update conflicted state in '%s'",
 +                                   path);
 +              }
 +              cleanly_resolved = !try_merge(id, path, &cur, &result);
 +              free(result.ptr);
 +              free(cur.ptr);
 +              if (cleanly_resolved)
 +                      break;
 +      }
 +
 +      if (id->collection->status_nr <= id->variant)
 +              return error("no remembered resolution for '%s'", path);
 +
        filename = rerere_path(id, "postimage");
        if (unlink(filename))
                return (errno == ENOENT
                        ? error("no remembered resolution for %s", path)
-                       : error("cannot unlink %s: %s", filename, strerror(errno)));
+                       : error_errno("cannot unlink %s", filename));
  
        /*
         * Update the preimage so that the user can resolve the
@@@ -1126,16 -893,29 +1122,16 @@@ int rerere_forget(struct pathspec *path
   * Garbage collection support
   */
  
 -/*
 - * Note that this is not reentrant but is used only one-at-a-time
 - * so it does not matter right now.
 - */
 -static struct rerere_id *dirname_to_id(const char *name)
 -{
 -      static struct rerere_id id;
 -      xsnprintf(id.hex, sizeof(id.hex), "%s", name);
 -      return &id;
 -}
 -
 -static time_t rerere_created_at(const char *dir_name)
 +static time_t rerere_created_at(struct rerere_id *id)
  {
        struct stat st;
 -      struct rerere_id *id = dirname_to_id(dir_name);
  
        return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
  }
  
 -static time_t rerere_last_used_at(const char *dir_name)
 +static time_t rerere_last_used_at(struct rerere_id *id)
  {
        struct stat st;
 -      struct rerere_id *id = dirname_to_id(dir_name);
  
        return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
  }
   */
  static void unlink_rr_item(struct rerere_id *id)
  {
 -      unlink(rerere_path(id, "thisimage"));
 -      unlink(rerere_path(id, "preimage"));
 -      unlink(rerere_path(id, "postimage"));
 -      /*
 -       * NEEDSWORK: what if this rmdir() fails?  Wouldn't we then
 -       * assume that we already have preimage recorded in
 -       * do_plain_rerere()?
 -       */
 -      rmdir(rerere_path(id, NULL));
 +      unlink_or_warn(rerere_path(id, "thisimage"));
 +      remove_variant(id);
 +      id->collection->status[id->variant] = 0;
 +}
 +
 +static void prune_one(struct rerere_id *id, time_t now,
 +                    int cutoff_resolve, int cutoff_noresolve)
 +{
 +      time_t then;
 +      int cutoff;
 +
 +      then = rerere_last_used_at(id);
 +      if (then)
 +              cutoff = cutoff_resolve;
 +      else {
 +              then = rerere_created_at(id);
 +              if (!then)
 +                      return;
 +              cutoff = cutoff_noresolve;
 +      }
 +      if (then < now - cutoff * 86400)
 +              unlink_rr_item(id);
  }
  
  void rerere_gc(struct string_list *rr)
        struct string_list to_remove = STRING_LIST_INIT_DUP;
        DIR *dir;
        struct dirent *e;
 -      int i, cutoff;
 -      time_t now = time(NULL), then;
 +      int i;
 +      time_t now = time(NULL);
        int cutoff_noresolve = 15;
        int cutoff_resolve = 60;
  
                die_errno("unable to open rr-cache directory");
        /* Collect stale conflict IDs ... */
        while ((e = readdir(dir))) {
 +              struct rerere_dir *rr_dir;
 +              struct rerere_id id;
 +              int now_empty;
 +
                if (is_dot_or_dotdot(e->d_name))
                        continue;
 -
 -              then = rerere_last_used_at(e->d_name);
 -              if (then) {
 -                      cutoff = cutoff_resolve;
 -              } else {
 -                      then = rerere_created_at(e->d_name);
 -                      if (!then)
 -                              continue;
 -                      cutoff = cutoff_noresolve;
 +              rr_dir = find_rerere_dir(e->d_name);
 +              if (!rr_dir)
 +                      continue; /* or should we remove e->d_name? */
 +
 +              now_empty = 1;
 +              for (id.variant = 0, id.collection = rr_dir;
 +                   id.variant < id.collection->status_nr;
 +                   id.variant++) {
 +                      prune_one(&id, now, cutoff_resolve, cutoff_noresolve);
 +                      if (id.collection->status[id.variant])
 +                              now_empty = 0;
                }
 -              if (then < now - cutoff * 86400)
 +              if (now_empty)
                        string_list_append(&to_remove, e->d_name);
        }
        closedir(dir);
 -      /* ... and then remove them one-by-one */
 +
 +      /* ... and then remove the empty directories */
        for (i = 0; i < to_remove.nr; i++)
 -              unlink_rr_item(dirname_to_id(to_remove.items[i].string));
 +              rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
        string_list_clear(&to_remove, 0);
        rollback_lock_file(&write_lock);
  }
@@@ -1236,10 -996,8 +1232,10 @@@ void rerere_clear(struct string_list *m
  
        for (i = 0; i < merge_rr->nr; i++) {
                struct rerere_id *id = merge_rr->items[i].util;
 -              if (!has_rerere_resolution(id))
 +              if (!has_rerere_resolution(id)) {
                        unlink_rr_item(id);
 +                      rmdir(rerere_path(id, NULL));
 +              }
        }
        unlink_or_warn(git_path_merge_rr());
        rollback_lock_file(&write_lock);
diff --combined run-command.c
index f5c57a5fc77d53d5fba7a494113bb999455fac60,d645a8d8bc3c18887c65bf88deeaaac95cd50970..af0c8a10df0ca06e900d26486fa6c1a86bb2ff7f
@@@ -233,7 -233,7 +233,7 @@@ static int wait_or_whine(pid_t pid, con
  
        if (waiting < 0) {
                failed_errno = errno;
-               error("waitpid for %s failed: %s", argv0, strerror(errno));
+               error_errno("waitpid for %s failed", argv0);
        } else if (waiting != pid) {
                error("waitpid is confused (%s)", argv0);
        } else if (WIFSIGNALED(status)) {
@@@ -420,8 -420,7 +420,7 @@@ fail_pipe
                }
        }
        if (cmd->pid < 0)
-               error("cannot fork() for %s: %s", cmd->argv[0],
-                       strerror(errno));
+               error_errno("cannot fork() for %s", cmd->argv[0]);
        else if (cmd->clean_on_exit)
                mark_child_for_cleanup(cmd->pid);
  
                        cmd->dir, fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
-               error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
+               error_errno("cannot spawn %s", cmd->argv[0]);
        if (cmd->clean_on_exit && cmd->pid >= 0)
                mark_child_for_cleanup(cmd->pid);
  
@@@ -590,16 -589,6 +589,16 @@@ static void *run_thread(void *data
        struct async *async = data;
        intptr_t ret;
  
 +      if (async->isolate_sigpipe) {
 +              sigset_t mask;
 +              sigemptyset(&mask);
 +              sigaddset(&mask, SIGPIPE);
 +              if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
 +                      ret = error("unable to block SIGPIPE in async thread");
 +                      return (void *)ret;
 +              }
 +      }
 +
        pthread_setspecific(async_key, async);
        ret = async->proc(async->proc_in, async->proc_out, async->data);
        return (void *)ret;
@@@ -703,7 -692,7 +702,7 @@@ int start_async(struct async *async
                if (pipe(fdin) < 0) {
                        if (async->out > 0)
                                close(async->out);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->in = fdin[1];
        }
                                close_pair(fdin);
                        else if (async->in)
                                close(async->in);
-                       return error("cannot create pipe: %s", strerror(errno));
+                       return error_errno("cannot create pipe");
                }
                async->out = fdout[0];
        }
  
        async->pid = fork();
        if (async->pid < 0) {
-               error("fork (async) failed: %s", strerror(errno));
+               error_errno("fork (async) failed");
                goto error;
        }
        if (!async->pid) {
        {
                int err = pthread_create(&async->tid, NULL, run_thread, async);
                if (err) {
-                       error("cannot create thread: %s", strerror(err));
+                       error_errno("cannot create thread");
                        goto error;
                }
        }
@@@ -825,10 -814,7 +824,10 @@@ const char *find_hook(const char *name
        static struct strbuf path = STRBUF_INIT;
  
        strbuf_reset(&path);
 -      strbuf_git_path(&path, "hooks/%s", name);
 +      if (git_hooks_path)
 +              strbuf_addf(&path, "%s/%s", git_hooks_path, name);
 +      else
 +              strbuf_git_path(&path, "hooks/%s", name);
        if (access(path.buf, X_OK) < 0)
                return NULL;
        return path.buf;
@@@ -915,7 -901,7 +914,7 @@@ struct parallel_processes 
        struct strbuf buffered_output; /* of finished children */
  };
  
 -static int default_start_failure(struct strbuf *err,
 +static int default_start_failure(struct strbuf *out,
                                 void *pp_cb,
                                 void *pp_task_cb)
  {
  }
  
  static int default_task_finished(int result,
 -                               struct strbuf *err,
 +                               struct strbuf *out,
                                 void *pp_cb,
                                 void *pp_task_cb)
  {
@@@ -1007,7 -993,7 +1006,7 @@@ static void pp_cleanup(struct parallel_
         * When get_next_task added messages to the buffer in its last
         * iteration, the buffered output is non empty.
         */
 -      fputs(pp->buffered_output.buf, stderr);
 +      strbuf_write(&pp->buffered_output, stderr);
        strbuf_release(&pp->buffered_output);
  
        sigchain_pop_common();
@@@ -1092,7 -1078,7 +1091,7 @@@ static void pp_output(struct parallel_p
        int i = pp->output_owner;
        if (pp->children[i].state == GIT_CP_WORKING &&
            pp->children[i].err.len) {
 -              fputs(pp->children[i].err.buf, stderr);
 +              strbuf_write(&pp->children[i].err, stderr);
                strbuf_reset(&pp->children[i].err);
        }
  }
@@@ -1130,11 -1116,11 +1129,11 @@@ static int pp_collect_finished(struct p
                        strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                        strbuf_reset(&pp->children[i].err);
                } else {
 -                      fputs(pp->children[i].err.buf, stderr);
 +                      strbuf_write(&pp->children[i].err, stderr);
                        strbuf_reset(&pp->children[i].err);
  
                        /* Output all other finished child processes */
 -                      fputs(pp->buffered_output.buf, stderr);
 +                      strbuf_write(&pp->buffered_output, stderr);
                        strbuf_reset(&pp->buffered_output);
  
                        /*
diff --combined transport-helper.c
index 13b7a57a759c8f15d92cd3f61e22cdd0010db570,f09fadcb194c2c0d39e8ddef4987c68184ee02e9..bd666b29ec12d4657d1939a6b7de61f55d52bd1f
@@@ -1152,7 -1152,7 +1152,7 @@@ static void udt_close_if_finished(struc
  }
  
  /*
 - * Tries to read read data from source into buffer. If buffer is full,
 + * Tries to read data from source into buffer. If buffer is full,
   * no data is read. Returns 0 on success, -1 on error.
   */
  static int udt_do_read(struct unidirectional_transfer *t)
        bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
                errno != EINTR) {
-               error("read(%s) failed: %s", t->src_name, strerror(errno));
+               error_errno("read(%s) failed", t->src_name);
                return -1;
        } else if (bytes == 0) {
                transfer_debug("%s EOF (with %i bytes in buffer)",
@@@ -1193,7 -1193,7 +1193,7 @@@ static int udt_do_write(struct unidirec
        transfer_debug("%s is writable", t->dest_name);
        bytes = xwrite(t->dest, t->buf, t->bufuse);
        if (bytes < 0 && errno != EWOULDBLOCK) {
-               error("write(%s) failed: %s", t->dest_name, strerror(errno));
+               error_errno("write(%s) failed", t->dest_name);
                return -1;
        } else if (bytes > 0) {
                t->bufuse -= bytes;
@@@ -1306,7 -1306,7 +1306,7 @@@ static int tloop_join(pid_t pid, const 
  {
        int tret;
        if (waitpid(pid, &tret, 0) < 0) {
-               error("%s process failed to wait: %s", name, strerror(errno));
+               error_errno("%s process failed to wait", name);
                return 1;
        }
        if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
diff --combined unpack-trees.c
index e81024bb9e43d67ecd126359f511e642bf89c11b,bb0d14210c37431297d36be09da64ab5093563f9..6bc9512a4516396c7ac0cc0fd5b5cebb01c60cbd
@@@ -58,74 -58,40 +58,74 @@@ void setup_unpack_trees_porcelain(struc
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
 -      const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
  
 -      if (advice_commit_before_merge)
 -              msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
 -                      "Please, commit your changes or stash them before you can %s.";
 +      if (!strcmp(cmd, "checkout"))
 +              msg = advice_commit_before_merge
 +                    ? _("Your local changes to the following files would be overwritten by checkout:\n%%s"
 +                        "Please commit your changes or stash them before you can switch branches.")
 +                    : _("Your local changes to the following files would be overwritten by checkout:\n%%s");
 +      else if (!strcmp(cmd, "merge"))
 +              msg = advice_commit_before_merge
 +                    ? _("Your local changes to the following files would be overwritten by merge:\n%%s"
 +                        "Please commit your changes or stash them before you can merge.")
 +                    : _("Your local changes to the following files would be overwritten by merge:\n%%s");
        else
 -              msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
 +              msg = advice_commit_before_merge
 +                    ? _("Your local changes to the following files would be overwritten by %s:\n%%s"
 +                        "Please commit your changes or stash them before you can %s.")
 +                    : _("Your local changes to the following files would be overwritten by %s:\n%%s");
        msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
 -              xstrfmt(msg, cmd, cmd2);
 +              xstrfmt(msg, cmd, cmd);
  
        msgs[ERROR_NOT_UPTODATE_DIR] =
 -              "Updating the following directories would lose untracked files in it:\n%s";
 -
 -      if (advice_commit_before_merge)
 -              msg = "The following untracked working tree files would be %s by %s:\n%%s"
 -                      "Please move or remove them before you can %s.";
 +              _("Updating the following directories would lose untracked files in it:\n%s");
 +
 +      if (!strcmp(cmd, "checkout"))
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be removed by checkout:\n%%s"
 +                        "Please move or remove them before you can switch branches.")
 +                    : _("The following untracked working tree files would be removed by checkout:\n%%s");
 +      else if (!strcmp(cmd, "merge"))
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be removed by merge:\n%%s"
 +                        "Please move or remove them before you can merge.")
 +                    : _("The following untracked working tree files would be removed by merge:\n%%s");
        else
 -              msg = "The following untracked working tree files would be %s by %s:\n%%s";
 -
 -      msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
 -      msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be removed by %s:\n%%s"
 +                        "Please move or remove them before you can %s.")
 +                    : _("The following untracked working tree files would be removed by %s:\n%%s");
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd);
 +
 +      if (!strcmp(cmd, "checkout"))
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be overwritten by checkout:\n%%s"
 +                        "Please move or remove them before you can switch branches.")
 +                    : _("The following untracked working tree files would be overwritten by checkout:\n%%s");
 +      else if (!strcmp(cmd, "merge"))
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be overwritten by merge:\n%%s"
 +                        "Please move or remove them before you can merge.")
 +                    : _("The following untracked working tree files would be overwritten by merge:\n%%s");
 +      else
 +              msg = advice_commit_before_merge
 +                    ? _("The following untracked working tree files would be overwritten by %s:\n%%s"
 +                        "Please move or remove them before you can %s.")
 +                    : _("The following untracked working tree files would be overwritten by %s:\n%%s");
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd);
  
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
         * cannot easily display it as a list.
         */
 -      msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
 +      msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'.  Cannot bind.");
  
        msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
 -              "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
 +              _("Cannot update sparse checkout: the following entries are not up-to-date:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
 -              "The following Working tree files would be overwritten by sparse checkout update:\n%s";
 +              _("The following Working tree files would be overwritten by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
 -              "The following Working tree files would be removed by sparse checkout update:\n%s";
 +              _("The following Working tree files would be removed by sparse checkout update:\n%s");
  
        opts->show_all_errors = 1;
        /* rejected paths may not have a static buffer */
@@@ -202,7 -168,7 +202,7 @@@ static void display_error_msgs(struct u
                string_list_clear(rejects, 0);
        }
        if (something_displayed)
 -              fprintf(stderr, "Aborting\n");
 +              fprintf(stderr, _("Aborting\n"));
  }
  
  /*
@@@ -509,7 -475,7 +509,7 @@@ static int traverse_trees_recursive(in
        for (i = 0; i < n; i++, dirmask >>= 1) {
                const unsigned char *sha1 = NULL;
                if (dirmask & 1)
 -                      sha1 = names[i].sha1;
 +                      sha1 = names[i].oid->hash;
                buf[i] = fill_tree_descriptor(t+i, sha1);
        }
  
@@@ -625,7 -591,7 +625,7 @@@ static struct cache_entry *create_ce_en
        ce->ce_mode = create_ce_mode(n->mode);
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
 -      hashcpy(ce->sha1, n->sha1);
 +      hashcpy(ce->sha1, n->oid->hash);
        make_traverse_path(ce->name, info, n);
  
        return ce;
@@@ -1533,8 -1499,7 +1533,7 @@@ static int verify_absent_1(const struc
  
                path = xmemdupz(ce->name, len);
                if (lstat(path, &st))
-                       ret = error("cannot stat '%s': %s", path,
-                                       strerror(errno));
+                       ret = error_errno("cannot stat '%s'", path);
                else
                        ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
                                                 &st, error_type, o);
                return ret;
        } else if (lstat(ce->name, &st)) {
                if (errno != ENOENT)
-                       return error("cannot stat '%s': %s", ce->name,
-                                    strerror(errno));
+                       return error_errno("cannot stat '%s'", ce->name);
                return 0;
        } else {
                return check_ok_to_remove(ce->name, ce_namelen(ce),
diff --combined upload-pack.c
index dc802a07c2225463c2e4ee923b9cf144d57a00cc,b1c3e76d926494d78f1254c2f15003f1367b69dc..f19444df7bc9c19da4d2f3bf831525f7a5d6034d
@@@ -90,32 -90,35 +90,32 @@@ static void create_pack_file(void
                "corruption on the remote side.";
        int buffered = -1;
        ssize_t sz;
 -      const char *argv[13];
 -      int i, arg = 0;
 +      int i;
        FILE *pipe_fd;
  
        if (shallow_nr) {
 -              argv[arg++] = "--shallow-file";
 -              argv[arg++] = "";
 +              argv_array_push(&pack_objects.args, "--shallow-file");
 +              argv_array_push(&pack_objects.args, "");
        }
 -      argv[arg++] = "pack-objects";
 -      argv[arg++] = "--revs";
 +      argv_array_push(&pack_objects.args, "pack-objects");
 +      argv_array_push(&pack_objects.args, "--revs");
        if (use_thin_pack)
 -              argv[arg++] = "--thin";
 +              argv_array_push(&pack_objects.args, "--thin");
  
 -      argv[arg++] = "--stdout";
 +      argv_array_push(&pack_objects.args, "--stdout");
        if (shallow_nr)
 -              argv[arg++] = "--shallow";
 +              argv_array_push(&pack_objects.args, "--shallow");
        if (!no_progress)
 -              argv[arg++] = "--progress";
 +              argv_array_push(&pack_objects.args, "--progress");
        if (use_ofs_delta)
 -              argv[arg++] = "--delta-base-offset";
 +              argv_array_push(&pack_objects.args, "--delta-base-offset");
        if (use_include_tag)
 -              argv[arg++] = "--include-tag";
 -      argv[arg++] = NULL;
 +              argv_array_push(&pack_objects.args, "--include-tag");
  
        pack_objects.in = -1;
        pack_objects.out = -1;
        pack_objects.err = -1;
        pack_objects.git_cmd = 1;
 -      pack_objects.argv = argv;
  
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
  
                if (ret < 0) {
                        if (errno != EINTR) {
-                               error("poll failed, resuming: %s",
-                                     strerror(errno));
+                               error_errno("poll failed, resuming");
                                sleep(1);
                        }
                        continue;
diff --combined wrapper.c
index 9009f8bd3d32a768454ad5fbe6cb8f7b88781a24,3df2fe0e5eb6b49bca5994338094e39982a161cf..5dc4e15aa9bf73bb2ce0a7af1837e092e0cab585
+++ b/wrapper.c
@@@ -446,6 -446,23 +446,6 @@@ int git_mkstemp(char *path, size_t len
        return mkstemp(path);
  }
  
 -/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
 -int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
 -{
 -      const char *tmp;
 -      size_t n;
 -
 -      tmp = getenv("TMPDIR");
 -      if (!tmp)
 -              tmp = "/tmp";
 -      n = snprintf(path, len, "%s/%s", tmp, template);
 -      if (len <= n) {
 -              errno = ENAMETOOLONG;
 -              return -1;
 -      }
 -      return mkstemps(path, suffix_len);
 -}
 -
  /* Adapted from libiberty's mkstemp.c. */
  
  #undef TMP_MAX
@@@ -555,7 -572,7 +555,7 @@@ static int warn_if_unremovable(const ch
        if (!rc || errno == ENOENT)
                return 0;
        err = errno;
-       warning("unable to %s %s: %s", op, file, strerror(errno));
+       warning_errno("unable to %s %s", op, file);
        errno = err;
        return rc;
  }
@@@ -591,7 -608,7 +591,7 @@@ int remove_or_warn(unsigned int mode, c
  
  void warn_on_inaccessible(const char *path)
  {
-       warning(_("unable to access '%s': %s"), path, strerror(errno));
+       warning_errno(_("unable to access '%s'"), path);
  }
  
  static int access_error_is_ok(int err, unsigned flag)