Merge branch 'dt/xgethostname-nul-termination'
authorJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:57 +0000 (22:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:57 +0000 (22:07 -0700)
gethostname(2) may not NUL terminate the buffer if hostname does
not fit; unfortunately there is no easy way to see if our buffer
was too small, but at least this will make sure we will not end up
using garbage past the end of the buffer.

* dt/xgethostname-nul-termination:
xgethostname: handle long hostnames
use HOST_NAME_MAX to size buffers for gethostname(2)

1  2 
builtin/gc.c
builtin/receive-pack.c
daemon.c
fetch-pack.c
git-compat-util.h
ident.c
wrapper.c
diff --combined builtin/gc.c
index 2daede78205bd065f907cb153b7729613fcfd82c,4f85610d87697e67368a57baa5550108fdeb6d10..91f7696a85ec974d0ff5a1591cc404ed99efb7d7
@@@ -33,8 -33,6 +33,8 @@@ static int aggressive_window = 250
  static int gc_auto_threshold = 6700;
  static int gc_auto_pack_limit = 50;
  static int detach_auto = 1;
 +static unsigned long gc_log_expire_time;
 +static const char *gc_log_expire = "1.day.ago";
  static const char *prune_expire = "2.weeks.ago";
  static const char *prune_worktrees_expire = "3.months.ago";
  
@@@ -64,31 -62,24 +64,31 @@@ static void report_pack_garbage(unsigne
                string_list_append(&pack_garbage, path);
  }
  
 -static void git_config_date_string(const char *key, const char **output)
 -{
 -      if (git_config_get_string_const(key, output))
 -              return;
 -      if (strcmp(*output, "now")) {
 -              unsigned long now = approxidate("now");
 -              if (approxidate(*output) >= now)
 -                      git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 -      }
 -}
 -
  static void process_log_file(void)
  {
        struct stat st;
 -      if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
 +      if (fstat(get_lock_file_fd(&log_lock), &st)) {
 +              /*
 +               * Perhaps there was an i/o error or another
 +               * unlikely situation.  Try to make a note of
 +               * this in gc.log along with any existing
 +               * messages.
 +               */
 +              int saved_errno = errno;
 +              fprintf(stderr, _("Failed to fstat %s: %s"),
 +                      get_tempfile_path(&log_lock.tempfile),
 +                      strerror(saved_errno));
 +              fflush(stderr);
                commit_lock_file(&log_lock);
 -      else
 +              errno = saved_errno;
 +      } else if (st.st_size) {
 +              /* There was some error recorded in the lock file */
 +              commit_lock_file(&log_lock);
 +      } else {
 +              /* No error, clean up any old gc.log */
 +              unlink(git_path("gc.log"));
                rollback_lock_file(&log_lock);
 +      }
  }
  
  static void process_log_file_at_exit(void)
@@@ -120,10 -111,8 +120,10 @@@ static void gc_config(void
        git_config_get_int("gc.auto", &gc_auto_threshold);
        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.worktreepruneexpire", &prune_worktrees_expire);
 +      git_config_get_expiry("gc.pruneexpire", &prune_expire);
 +      git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
 +      git_config_get_expiry("gc.logexpiry", &gc_log_expire);
 +
        git_config(git_default_config, NULL);
  }
  
@@@ -135,6 -124,8 +135,6 @@@ static int too_many_loose_objects(void
         * distributed, we can check only one and get a reasonable
         * estimate.
         */
 -      char path[PATH_MAX];
 -      const char *objdir = get_object_directory();
        DIR *dir;
        struct dirent *ent;
        int auto_threshold;
        if (gc_auto_threshold <= 0)
                return 0;
  
 -      if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
 -              warning(_("insanely long object directory %.*s"), 50, objdir);
 -              return 0;
 -      }
 -      dir = opendir(path);
 +      dir = opendir(git_path("objects/17"));
        if (!dir)
                return 0;
  
@@@ -196,11 -191,6 +196,11 @@@ static void add_repack_all_option(void
        }
  }
  
 +static void add_repack_incremental_option(void)
 +{
 +       argv_array_push(&repack, "--no-write-bitmap-index");
 +}
 +
  static int need_to_gc(void)
  {
        /*
         */
        if (too_many_packs())
                add_repack_all_option();
 -      else if (!too_many_loose_objects())
 +      else if (too_many_loose_objects())
 +              add_repack_incremental_option();
 +      else
                return 0;
  
        if (run_hook_le(NULL, "pre-auto-gc", NULL))
  static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
  {
        static struct lock_file lock;
-       char my_host[128];
+       char my_host[HOST_NAME_MAX + 1];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
                /* already locked */
                return NULL;
  
-       if (gethostname(my_host, sizeof(my_host)))
+       if (xgethostname(my_host, sizeof(my_host)))
                xsnprintf(my_host, sizeof(my_host), "unknown");
  
        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];
+               static char locking_host[HOST_NAME_MAX + 1];
+               static char *scan_fmt;
                int should_exit;
+               if (!scan_fmt)
+                       scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
                fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
                         * running.
                         */
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
-                       fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
+                       fscanf(fp, scan_fmt, &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
                        (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
  static int report_last_gc_error(void)
  {
        struct strbuf sb = STRBUF_INIT;
 -      int ret;
 +      int ret = 0;
 +      struct stat st;
 +      char *gc_log_path = git_pathdup("gc.log");
 +
 +      if (stat(gc_log_path, &st)) {
 +              if (errno == ENOENT)
 +                      goto done;
  
 -      ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
 +              ret = error_errno(_("Can't stat %s"), gc_log_path);
 +              goto done;
 +      }
 +
 +      if (st.st_mtime < gc_log_expire_time)
 +              goto done;
 +
 +      ret = strbuf_read_file(&sb, gc_log_path, 0);
        if (ret > 0)
 -              return error(_("The last gc run reported the following. "
 +              ret = error(_("The last gc run reported the following. "
                               "Please correct the root cause\n"
                               "and remove %s.\n"
                               "Automatic cleanup will not be performed "
                               "until the file is removed.\n\n"
                               "%s"),
 -                           git_path("gc.log"), sb.buf);
 +                          gc_log_path, sb.buf);
        strbuf_release(&sb);
 -      return 0;
 +done:
 +      free(gc_log_path);
 +      return ret;
  }
  
  static int gc_before_repack(void)
@@@ -369,10 -346,7 +373,10 @@@ int cmd_gc(int argc, const char **argv
        argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
        argv_array_pushl(&rerere, "rerere", "gc", NULL);
  
 +      /* default expiry time, overwritten in gc_config */
        gc_config();
 +      if (parse_expiry_date(gc_log_expire, &gc_log_expire_time))
 +              die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire);
  
        if (pack_refs < 0)
                pack_refs = !is_bare_repository();
                warning(_("There are too many unreachable loose objects; "
                        "run 'git prune' to remove them."));
  
 +      if (!daemonized)
 +              unlink(git_path("gc.log"));
 +
        return 0;
  }
diff --combined builtin/receive-pack.c
index 7f484e7f6b41be0555babb54ba8853d0bf1e4269,eee5faaa244ec0252305adbeddf7d5c5fa2d4eb0..f96834f42c9849746b64c20c84869232cbac7367
@@@ -20,8 -20,6 +20,8 @@@
  #include "gpg-interface.h"
  #include "sigchain.h"
  #include "fsck.h"
 +#include "tmp-objdir.h"
 +#include "oidset.h"
  
  static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@@ -48,7 -46,6 +48,7 @@@ static int transfer_unpack_limit = -1
  static int advertise_atomic_push = 1;
  static int advertise_push_options;
  static int unpack_limit = 100;
 +static off_t max_input_size;
  static int report_status;
  static int use_sideband;
  static int use_atomic;
@@@ -88,8 -85,6 +88,8 @@@ static enum 
  } use_keepalive;
  static int keepalive_in_sec = 5;
  
 +static struct tmp_objdir *tmp_objdir;
 +
  static enum deny_action parse_deny_action(const char *var, const char *value)
  {
        if (value) {
@@@ -217,18 -212,13 +217,18 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
 +      if (strcmp(var, "receive.maxinputsize") == 0) {
 +              max_input_size = git_config_int64(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, cb);
  }
  
 -static void show_ref(const char *path, const unsigned char *sha1)
 +static void show_ref(const char *path, const struct object_id *oid)
  {
        if (sent_capabilities) {
 -              packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
 +              packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path);
        } else {
                struct strbuf cap = STRBUF_INIT;
  
                if (advertise_push_options)
                        strbuf_addstr(&cap, " push-options");
                strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
 -              packet_write(1, "%s %s%c%s\n",
 -                           sha1_to_hex(sha1), path, 0, cap.buf);
 +              packet_write_fmt(1, "%s %s%c%s\n",
 +                           oid_to_hex(oid), path, 0, cap.buf);
                strbuf_release(&cap);
                sent_capabilities = 1;
        }
  }
  
  static int show_ref_cb(const char *path_full, const struct object_id *oid,
 -                     int flag, void *unused)
 +                     int flag, void *data)
  {
 +      struct oidset *seen = data;
        const char *path = strip_namespace(path_full);
  
        if (ref_is_hidden(path, path_full))
        /*
         * Advertise refs outside our current namespace as ".have"
         * refs, so that the client can use them to minimize data
 -       * transfer but will otherwise ignore them. This happens to
 -       * cover ".have" that are thrown in by add_one_alternate_ref()
 -       * to mark histories that are complete in our alternates as
 -       * well.
 +       * transfer but will otherwise ignore them.
         */
 -      if (!path)
 +      if (!path) {
 +              if (oidset_insert(seen, oid))
 +                      return 0;
                path = ".have";
 -      show_ref(path, oid->hash);
 +      } else {
 +              oidset_insert(seen, oid);
 +      }
 +      show_ref(path, oid);
        return 0;
  }
  
 -static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 +static void show_one_alternate_ref(const char *refname,
 +                                 const struct object_id *oid,
 +                                 void *data)
  {
 -      show_ref(".have", sha1);
 -}
 +      struct oidset *seen = data;
  
 -static void collect_one_alternate_ref(const struct ref *ref, void *data)
 -{
 -      struct sha1_array *sa = data;
 -      sha1_array_append(sa, ref->old_oid.hash);
 +      if (oidset_insert(seen, oid))
 +              return;
 +
 +      show_ref(".have", oid);
  }
  
  static void write_head_info(void)
  {
 -      struct sha1_array sa = SHA1_ARRAY_INIT;
 +      static struct oidset seen = OIDSET_INIT;
  
 -      for_each_alternate_ref(collect_one_alternate_ref, &sa);
 -      sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
 -      sha1_array_clear(&sa);
 -      for_each_ref(show_ref_cb, NULL);
 +      for_each_ref(show_ref_cb, &seen);
 +      for_each_alternate_ref(show_one_alternate_ref, &seen);
 +      oidset_clear(&seen);
        if (!sent_capabilities)
 -              show_ref("capabilities^{}", null_sha1);
 +              show_ref("capabilities^{}", &null_oid);
  
        advertise_shallow_grafts(1);
  
@@@ -309,8 -296,8 +309,8 @@@ struct command 
        unsigned int skip_update:1,
                     did_not_exist:1;
        int index;
 -      unsigned char old_sha1[20];
 -      unsigned char new_sha1[20];
 +      struct object_id old_oid;
 +      struct object_id new_oid;
        char ref_name[FLEX_ARRAY]; /* more */
  };
  
@@@ -670,9 -657,6 +670,9 @@@ static int run_and_feed_hook(const cha
        } else
                argv_array_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
  
 +      if (tmp_objdir)
 +              argv_array_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir));
 +
        if (use_sideband) {
                memset(&muxer, 0, sizeof(muxer));
                muxer.proc = copy_to_sideband;
@@@ -723,7 -707,7 +723,7 @@@ static int feed_receive_hook(void *stat
                return -1; /* EOF */
        strbuf_reset(&state->buf);
        strbuf_addf(&state->buf, "%s %s %s\n",
 -                  sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
 +                  oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
                    cmd->ref_name);
        state->cmd = cmd->next;
        if (bufp) {
@@@ -764,8 -748,8 +764,8 @@@ static int run_update_hook(struct comma
                return 0;
  
        argv[1] = cmd->ref_name;
 -      argv[2] = sha1_to_hex(cmd->old_sha1);
 -      argv[3] = sha1_to_hex(cmd->new_sha1);
 +      argv[2] = oid_to_hex(&cmd->old_oid);
 +      argv[3] = oid_to_hex(&cmd->new_oid);
        argv[4] = NULL;
  
        proc.no_stdin = 1;
@@@ -791,46 -775,54 +791,46 @@@ static int is_ref_checked_out(const cha
        return !strcmp(head_name, ref);
  }
  
 -static char *refuse_unconfigured_deny_msg[] = {
 -      "By default, updating the current branch in a non-bare repository",
 -      "is denied, because it will make the index and work tree inconsistent",
 -      "with what you pushed, and will require 'git reset --hard' to match",
 -      "the work tree to HEAD.",
 -      "",
 -      "You can set 'receive.denyCurrentBranch' configuration variable to",
 -      "'ignore' or 'warn' in the remote repository to allow pushing into",
 -      "its current branch; however, this is not recommended unless you",
 -      "arranged to update its work tree to match what you pushed in some",
 -      "other way.",
 -      "",
 -      "To squelch this message and still keep the default behaviour, set",
 -      "'receive.denyCurrentBranch' configuration variable to 'refuse'."
 -};
 +static char *refuse_unconfigured_deny_msg =
 +      N_("By default, updating the current branch in a non-bare repository\n"
 +         "is denied, because it will make the index and work tree inconsistent\n"
 +         "with what you pushed, and will require 'git reset --hard' to match\n"
 +         "the work tree to HEAD.\n"
 +         "\n"
 +         "You can set the 'receive.denyCurrentBranch' configuration variable\n"
 +         "to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
 +         "its current branch; however, this is not recommended unless you\n"
 +         "arranged to update its work tree to match what you pushed in some\n"
 +         "other way.\n"
 +         "\n"
 +         "To squelch this message and still keep the default behaviour, set\n"
 +         "'receive.denyCurrentBranch' configuration variable to 'refuse'.");
  
  static void refuse_unconfigured_deny(void)
  {
 -      int i;
 -      for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
 -              rp_error("%s", refuse_unconfigured_deny_msg[i]);
 +      rp_error("%s", _(refuse_unconfigured_deny_msg));
  }
  
 -static char *refuse_unconfigured_deny_delete_current_msg[] = {
 -      "By default, deleting the current branch is denied, because the next",
 -      "'git clone' won't result in any file checked out, causing confusion.",
 -      "",
 -      "You can set 'receive.denyDeleteCurrent' configuration variable to",
 -      "'warn' or 'ignore' in the remote repository to allow deleting the",
 -      "current branch, with or without a warning message.",
 -      "",
 -      "To squelch this message, you can set it to 'refuse'."
 -};
 +static char *refuse_unconfigured_deny_delete_current_msg =
 +      N_("By default, deleting the current branch is denied, because the next\n"
 +         "'git clone' won't result in any file checked out, causing confusion.\n"
 +         "\n"
 +         "You can set 'receive.denyDeleteCurrent' configuration variable to\n"
 +         "'warn' or 'ignore' in the remote repository to allow deleting the\n"
 +         "current branch, with or without a warning message.\n"
 +         "\n"
 +         "To squelch this message, you can set it to 'refuse'.");
  
  static void refuse_unconfigured_deny_delete_current(void)
  {
 -      int i;
 -      for (i = 0;
 -           i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
 -           i++)
 -              rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
 +      rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
  }
  
  static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
  static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
  {
        static struct lock_file shallow_lock;
 -      struct sha1_array extra = SHA1_ARRAY_INIT;
 +      struct oid_array extra = OID_ARRAY_INIT;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        uint32_t mask = 1 << (cmd->index % 32);
        int i;
                if (si->used_shallow[i] &&
                    (si->used_shallow[i][cmd->index / 32] & mask) &&
                    !delayed_reachability_test(si, i))
 -                      sha1_array_append(&extra, si->shallow->sha1[i]);
 +                      oid_array_append(&extra, &si->shallow->oid[i]);
  
 +      opt.env = tmp_objdir_env(tmp_objdir);
        setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
        if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
 -              sha1_array_clear(&extra);
 +              oid_array_clear(&extra);
                return -1;
        }
  
         * not lose these new roots..
         */
        for (i = 0; i < extra.nr; i++)
 -              register_shallow(extra.sha1[i]);
 +              register_shallow(extra.oid[i].hash);
  
        si->shallow_ref[cmd->index] = 0;
 -      sha1_array_clear(&extra);
 +      oid_array_clear(&extra);
        return 0;
  }
  
@@@ -987,8 -978,8 +987,8 @@@ static const char *update(struct comman
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
        const char *namespaced_name, *ret;
 -      unsigned char *old_sha1 = cmd->old_sha1;
 -      unsigned char *new_sha1 = cmd->new_sha1;
 +      struct object_id *old_oid = &cmd->old_oid;
 +      struct object_id *new_oid = &cmd->new_oid;
  
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
 -                      ret = update_worktree(new_sha1);
 +                      ret = update_worktree(new_oid->hash);
                        if (ret)
                                return ret;
                        break;
                }
        }
  
 -      if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
 +      if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
                error("unpack should have generated %s, "
 -                    "but I can't find it!", sha1_to_hex(new_sha1));
 +                    "but I can't find it!", oid_to_hex(new_oid));
                return "bad pack";
        }
  
 -      if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
 +      if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
                if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
        }
  
 -      if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
 -          !is_null_sha1(old_sha1) &&
 +      if (deny_non_fast_forwards && !is_null_oid(new_oid) &&
 +          !is_null_oid(old_oid) &&
            starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
  
 -              old_object = parse_object(old_sha1);
 -              new_object = parse_object(new_sha1);
 +              old_object = parse_object(old_oid->hash);
 +              new_object = parse_object(new_oid->hash);
  
                if (!old_object || !new_object ||
                    old_object->type != OBJ_COMMIT ||
                return "hook declined";
        }
  
 -      if (is_null_sha1(new_sha1)) {
 +      if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
 -              if (!parse_object(old_sha1)) {
 -                      old_sha1 = NULL;
 +              if (!parse_object(old_oid->hash)) {
 +                      old_oid = NULL;
                        if (ref_exists(name)) {
                                rp_warning("Allowing deletion of corrupt ref.");
                        } else {
                }
                if (ref_transaction_delete(transaction,
                                           namespaced_name,
 -                                         old_sha1,
 +                                         old_oid->hash,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
                        strbuf_release(&err);
  
                if (ref_transaction_update(transaction,
                                           namespaced_name,
 -                                         new_sha1, old_sha1,
 +                                         new_oid->hash, old_oid->hash,
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
  static void run_update_post_hook(struct command *commands)
  {
        struct command *cmd;
 -      int argc;
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *hook;
  
        hook = find_hook("post-update");
 -      for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
 -              if (cmd->error_string || cmd->did_not_exist)
 -                      continue;
 -              argc++;
 -      }
 -      if (!argc || !hook)
 +      if (!hook)
                return;
  
 -      argv_array_push(&proc.args, hook);
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
 +              if (!proc.args.argc)
 +                      argv_array_push(&proc.args, hook);
                argv_array_push(&proc.args, cmd->ref_name);
        }
 +      if (!proc.args.argc)
 +              return;
  
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
@@@ -1161,7 -1155,11 +1161,7 @@@ static void check_aliased_update(struc
        const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
 -      unsigned char sha1[GIT_SHA1_RAWSZ];
 -      char cmd_oldh[GIT_SHA1_HEXSZ + 1],
 -           cmd_newh[GIT_SHA1_HEXSZ + 1],
 -           dst_oldh[GIT_SHA1_HEXSZ + 1],
 -           dst_newh[GIT_SHA1_HEXSZ + 1];
 +      unsigned char sha1[GIT_MAX_RAWSZ];
        int flag;
  
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
  
        dst_cmd = (struct command *) item->util;
  
 -      if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
 -          !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
 +      if (!oidcmp(&cmd->old_oid, &dst_cmd->old_oid) &&
 +          !oidcmp(&cmd->new_oid, &dst_cmd->new_oid))
                return;
  
        dst_cmd->skip_update = 1;
  
 -      find_unique_abbrev_r(cmd_oldh, cmd->old_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(cmd_newh, cmd->new_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(dst_oldh, dst_cmd->old_sha1, DEFAULT_ABBREV);
 -      find_unique_abbrev_r(dst_newh, dst_cmd->new_sha1, DEFAULT_ABBREV);
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
 -               cmd->ref_name, cmd_oldh, cmd_newh,
 -               dst_cmd->ref_name, dst_oldh, dst_newh);
 +               cmd->ref_name,
 +               find_unique_abbrev(cmd->old_oid.hash, DEFAULT_ABBREV),
 +               find_unique_abbrev(cmd->new_oid.hash, DEFAULT_ABBREV),
 +               dst_cmd->ref_name,
 +               find_unique_abbrev(dst_cmd->old_oid.hash, DEFAULT_ABBREV),
 +               find_unique_abbrev(dst_cmd->new_oid.hash, DEFAULT_ABBREV));
  
        cmd->error_string = dst_cmd->error_string =
                "inconsistent aliased update";
@@@ -1230,10 -1228,10 +1230,10 @@@ static int command_singleton_iterator(v
        struct command **cmd_list = cb_data;
        struct command *cmd = *cmd_list;
  
 -      if (!cmd || is_null_sha1(cmd->new_sha1))
 +      if (!cmd || is_null_oid(&cmd->new_oid))
                return -1; /* end of list */
        *cmd_list = NULL; /* this returns only one */
 -      hashcpy(sha1, cmd->new_sha1);
 +      hashcpy(sha1, cmd->new_oid.hash);
        return 0;
  }
  
@@@ -1244,17 -1242,12 +1244,17 @@@ static void set_connectivity_errors(str
  
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct command *singleton = cmd;
 +              struct check_connected_options opt = CHECK_CONNECTED_INIT;
 +
                if (shallow_update && si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
 +
 +              opt.env = tmp_objdir_env(tmp_objdir);
                if (!check_connected(command_singleton_iterator, &singleton,
 -                                   NULL))
 +                                   &opt))
                        continue;
 +
                cmd->error_string = "missing necessary objects";
        }
  }
@@@ -1274,8 -1267,8 +1274,8 @@@ static int iterate_receive_command_list
                if (shallow_update && data->si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
 -              if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
 -                      hashcpy(sha1, cmd->new_sha1);
 +              if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
 +                      hashcpy(sha1, cmd->new_oid.hash);
                        *cmd_list = cmd->next;
                        return 0;
                }
@@@ -1302,7 -1295,7 +1302,7 @@@ static void reject_updates_to_hidden(st
  
                if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
                        continue;
 -              if (is_null_sha1(cmd->new_sha1))
 +              if (is_null_oid(&cmd->new_oid))
                        cmd->error_string = "deny deleting a hidden ref";
                else
                        cmd->error_string = "deny updating a hidden ref";
@@@ -1413,7 -1406,7 +1413,7 @@@ static void execute_commands(struct com
  {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct iterate_data data;
        struct async muxer;
        int err_fd = 0;
        data.si = si;
        opt.err_fd = err_fd;
        opt.progress = err_fd && !quiet;
 +      opt.env = tmp_objdir_env(tmp_objdir);
        if (check_connected(iterate_receive_command_list, &data, &opt))
                set_connectivity_errors(commands, si);
  
                return;
        }
  
 +      /*
 +       * Now we'll start writing out refs, which means the objects need
 +       * to be in their final positions so that other processes can see them.
 +       */
 +      if (tmp_objdir_migrate(tmp_objdir) < 0) {
 +              for (cmd = commands; cmd; cmd = cmd->next) {
 +                      if (!cmd->error_string)
 +                              cmd->error_string = "unable to migrate objects to permanent storage";
 +              }
 +              return;
 +      }
 +      tmp_objdir = NULL;
 +
        check_aliased_updates(commands);
  
        free(head_name_to_free);
 -      head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
 +      head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
  
        if (use_atomic)
                execute_commands_atomic(commands, si);
@@@ -1485,23 -1464,23 +1485,23 @@@ static struct command **queue_command(s
                                      const char *line,
                                      int linelen)
  {
 -      unsigned char old_sha1[20], new_sha1[20];
 +      struct object_id old_oid, new_oid;
        struct command *cmd;
        const char *refname;
        int reflen;
 +      const char *p;
  
 -      if (linelen < 83 ||
 -          line[40] != ' ' ||
 -          line[81] != ' ' ||
 -          get_sha1_hex(line, old_sha1) ||
 -          get_sha1_hex(line + 41, new_sha1))
 +      if (parse_oid_hex(line, &old_oid, &p) ||
 +          *p++ != ' ' ||
 +          parse_oid_hex(p, &new_oid, &p) ||
 +          *p++ != ' ')
                die("protocol error: expected old/new/ref, got '%s'", line);
  
 -      refname = line + 82;
 -      reflen = linelen - 82;
 +      refname = p;
 +      reflen = linelen - (p - line);
        FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen);
 -      hashcpy(cmd->old_sha1, old_sha1);
 -      hashcpy(cmd->new_sha1, new_sha1);
 +      oidcpy(&cmd->old_oid, &old_oid);
 +      oidcpy(&cmd->new_oid, &new_oid);
        *tail = cmd;
        return &cmd->next;
  }
@@@ -1523,12 -1502,12 +1523,12 @@@ static void queue_commands_from_cert(st
  
        while (boc < eoc) {
                const char *eol = memchr(boc, '\n', eoc - boc);
 -              tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol);
 +              tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc);
                boc = eol ? eol + 1 : eoc;
        }
  }
  
 -static struct command *read_head_info(struct sha1_array *shallow)
 +static struct command *read_head_info(struct oid_array *shallow)
  {
        struct command *commands = NULL;
        struct command **p = &commands;
                if (!line)
                        break;
  
 -              if (len == 48 && starts_with(line, "shallow ")) {
 -                      unsigned char sha1[20];
 -                      if (get_sha1_hex(line + 8, sha1))
 +              if (len 8 && starts_with(line, "shallow ")) {
 +                      struct object_id oid;
 +                      if (get_oid_hex(line + 8, &oid))
                                die("protocol error: expected shallow sha, got '%s'",
                                    line + 8);
 -                      sha1_array_append(shallow, sha1);
 +                      oid_array_append(shallow, &oid);
                        continue;
                }
  
@@@ -1633,17 -1612,12 +1633,17 @@@ static const char *parse_pack_header(st
  
  static const char *pack_lockfile;
  
 +static void push_header_arg(struct argv_array *args, struct pack_header *hdr)
 +{
 +      argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
 +                      ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
 +}
 +
  static const char *unpack(int err_fd, struct shallow_info *si)
  {
        struct pack_header hdr;
        const char *hdr_err;
        int status;
 -      char hdr_arg[38];
        struct child_process child = CHILD_PROCESS_INIT;
        int fsck_objects = (receive_fsck_objects >= 0
                            ? receive_fsck_objects
                        close(err_fd);
                return hdr_err;
        }
 -      snprintf(hdr_arg, sizeof(hdr_arg),
 -                      "--pack_header=%"PRIu32",%"PRIu32,
 -                      ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
  
        if (si->nr_ours || si->nr_theirs) {
                alt_shallow_file = setup_temporary_shallow(si->shallow);
                argv_array_push(&child.args, alt_shallow_file);
        }
  
 +      tmp_objdir = tmp_objdir_create();
 +      if (!tmp_objdir) {
 +              if (err_fd > 0)
 +                      close(err_fd);
 +              return "unable to create temporary object directory";
 +      }
 +      child.env = tmp_objdir_env(tmp_objdir);
 +
 +      /*
 +       * Normally we just pass the tmp_objdir environment to the child
 +       * processes that do the heavy lifting, but we may need to see these
 +       * objects ourselves to set up shallow information.
 +       */
 +      tmp_objdir_add_as_alternate(tmp_objdir);
 +
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
 -              argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
 +              argv_array_push(&child.args, "unpack-objects");
 +              push_header_arg(&child.args, &hdr);
                if (quiet)
                        argv_array_push(&child.args, "-q");
                if (fsck_objects)
                        argv_array_pushf(&child.args, "--strict%s",
                                fsck_msg_types.buf);
 +              if (max_input_size)
 +                      argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
 +                              (uintmax_t)max_input_size);
                child.no_stdout = 1;
                child.err = err_fd;
                child.git_cmd = 1;
                if (status)
                        return "unpack-objects abnormal exit";
        } else {
-               char hostname[256];
+               char hostname[HOST_NAME_MAX + 1];
  
 -              argv_array_pushl(&child.args, "index-pack",
 -                               "--stdin", hdr_arg, NULL);
 +              argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
 +              push_header_arg(&child.args, &hdr);
  
-               if (gethostname(hostname, sizeof(hostname)))
+               if (xgethostname(hostname, sizeof(hostname)))
                        xsnprintf(hostname, sizeof(hostname), "localhost");
                argv_array_pushf(&child.args,
                                 "--keep=receive-pack %"PRIuMAX" on %s",
                                fsck_msg_types.buf);
                if (!reject_thin)
                        argv_array_push(&child.args, "--fix-thin");
 +              if (max_input_size)
 +                      argv_array_pushf(&child.args, "--max-input-size=%"PRIuMAX,
 +                              (uintmax_t)max_input_size);
                child.out = -1;
                child.err = err_fd;
                child.git_cmd = 1;
@@@ -1806,7 -1761,7 +1806,7 @@@ static void prepare_shallow_update(stru
  
  static void update_shallow_info(struct command *commands,
                                struct shallow_info *si,
 -                              struct sha1_array *ref)
 +                              struct oid_array *ref)
  {
        struct command *cmd;
        int *ref_status;
        }
  
        for (cmd = commands; cmd; cmd = cmd->next) {
 -              if (is_null_sha1(cmd->new_sha1))
 +              if (is_null_oid(&cmd->new_oid))
                        continue;
 -              sha1_array_append(ref, cmd->new_sha1);
 +              oid_array_append(ref, &cmd->new_oid);
                cmd->index = ref->nr - 1;
        }
        si->ref = ref;
        ALLOC_ARRAY(ref_status, ref->nr);
        assign_shallow_commits_to_refs(si, NULL, ref_status);
        for (cmd = commands; cmd; cmd = cmd->next) {
 -              if (is_null_sha1(cmd->new_sha1))
 +              if (is_null_oid(&cmd->new_oid))
                        continue;
                if (ref_status[cmd->index]) {
                        cmd->error_string = "shallow update not allowed";
@@@ -1870,7 -1825,7 +1870,7 @@@ static int delete_only(struct command *
  {
        struct command *cmd;
        for (cmd = commands; cmd; cmd = cmd->next) {
 -              if (!is_null_sha1(cmd->new_sha1))
 +              if (!is_null_oid(&cmd->new_oid))
                        return 0;
        }
        return 1;
@@@ -1880,8 -1835,8 +1880,8 @@@ int cmd_receive_pack(int argc, const ch
  {
        int advertise_refs = 0;
        struct command *commands;
 -      struct sha1_array shallow = SHA1_ARRAY_INIT;
 -      struct sha1_array ref = SHA1_ARRAY_INIT;
 +      struct oid_array shallow = OID_ARRAY_INIT;
 +      struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
  
        struct option options[] = {
                run_receive_hook(commands, "post-receive", 1,
                                 &push_options);
                run_update_post_hook(commands);
 -              if (push_options.nr)
 -                      string_list_clear(&push_options, 0);
 +              string_list_clear(&push_options, 0);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,
        }
        if (use_sideband)
                packet_flush(1);
 -      sha1_array_clear(&shallow);
 -      sha1_array_clear(&ref);
 +      oid_array_clear(&shallow);
 +      oid_array_clear(&ref);
        free((void *)push_cert_nonce);
        return 0;
  }
diff --combined daemon.c
index f70d27b8260bf09443ea587d8fab61f97d66acfb,10cbc0fa5808d77d16b501371b6dde9f505b1e6e..ac7181a4832672ef1e599324a15868d1b06f8fc0
+++ b/daemon.c
@@@ -4,10 -4,6 +4,6 @@@
  #include "strbuf.h"
  #include "string-list.h"
  
- #ifndef HOST_NAME_MAX
- #define HOST_NAME_MAX 256
- #endif
  #ifdef NO_INITGROUPS
  #define initgroups(x, y) (0) /* nothing */
  #endif
@@@ -298,7 -294,7 +294,7 @@@ static int daemon_error(const char *dir
  {
        if (!informative_errors)
                msg = "access denied or repository not exported";
 -      packet_write(1, "ERR %s: %s", msg, dir);
 +      packet_write_fmt(1, "ERR %s: %s", msg, dir);
        return -1;
  }
  
@@@ -449,42 -445,46 +445,42 @@@ static void copy_to_log(int fd
        fclose(fp);
  }
  
 -static int run_service_command(const char **argv)
 +static int run_service_command(struct child_process *cld)
  {
 -      struct child_process cld = CHILD_PROCESS_INIT;
 -
 -      cld.argv = argv;
 -      cld.git_cmd = 1;
 -      cld.err = -1;
 -      if (start_command(&cld))
 +      argv_array_push(&cld->args, ".");
 +      cld->git_cmd = 1;
 +      cld->err = -1;
 +      if (start_command(cld))
                return -1;
  
        close(0);
        close(1);
  
 -      copy_to_log(cld.err);
 +      copy_to_log(cld->err);
  
 -      return finish_command(&cld);
 +      return finish_command(cld);
  }
  
  static int upload_pack(void)
  {
 -      /* Timeout as string */
 -      char timeout_buf[64];
 -      const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
 -
 -      argv[2] = timeout_buf;
 -
 -      snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
 -      return run_service_command(argv);
 +      struct child_process cld = CHILD_PROCESS_INIT;
 +      argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
 +      argv_array_pushf(&cld.args, "--timeout=%u", timeout);
 +      return run_service_command(&cld);
  }
  
  static int upload_archive(void)
  {
 -      static const char *argv[] = { "upload-archive", ".", NULL };
 -      return run_service_command(argv);
 +      struct child_process cld = CHILD_PROCESS_INIT;
 +      argv_array_push(&cld.args, "upload-archive");
 +      return run_service_command(&cld);
  }
  
  static int receive_pack(void)
  {
 -      static const char *argv[] = { "receive-pack", ".", NULL };
 -      return run_service_command(argv);
 +      struct child_process cld = CHILD_PROCESS_INIT;
 +      argv_array_push(&cld.args, "receive-pack");
 +      return run_service_command(&cld);
  }
  
  static struct daemon_service daemon_service[] = {
diff --combined fetch-pack.c
index c5e686706100fc5f6b3a3ede30b0c6ce6a8645d6,f43adfe7b55c3883d14449e7e577b7c65fac1320..afb8b05024823981be9bedfaa70f7301c6f7076c
@@@ -21,8 -21,6 +21,8 @@@ static int fetch_unpack_limit = -1
  static int unpack_limit = 100;
  static int prefer_ofs_delta = 1;
  static int no_done;
 +static int deepen_since_ok;
 +static int deepen_not_ok;
  static int fetch_fsck_objects = -1;
  static int transfer_fsck_objects = -1;
  static int agent_supported;
@@@ -35,7 -33,6 +35,7 @@@ static const char *alternate_shallow_fi
  #define COMMON_REF    (1U << 2)
  #define SEEN          (1U << 3)
  #define POPPED                (1U << 4)
 +#define ALTERNATE     (1U << 5)
  
  static int marked;
  
@@@ -53,56 -50,6 +53,56 @@@ static int non_common_revs, multi_ack, 
  #define ALLOW_REACHABLE_SHA1  02
  static unsigned int allow_unadvertised_object_request;
  
 +__attribute__((format (printf, 2, 3)))
 +static inline void print_verbose(const struct fetch_pack_args *args,
 +                               const char *fmt, ...)
 +{
 +      va_list params;
 +
 +      if (!args->verbose)
 +              return;
 +
 +      va_start(params, fmt);
 +      vfprintf(stderr, fmt, params);
 +      va_end(params);
 +      fputc('\n', stderr);
 +}
 +
 +struct alternate_object_cache {
 +      struct object **items;
 +      size_t nr, alloc;
 +};
 +
 +static void cache_one_alternate(const char *refname,
 +                              const struct object_id *oid,
 +                              void *vcache)
 +{
 +      struct alternate_object_cache *cache = vcache;
 +      struct object *obj = parse_object(oid->hash);
 +
 +      if (!obj || (obj->flags & ALTERNATE))
 +              return;
 +
 +      obj->flags |= ALTERNATE;
 +      ALLOC_GROW(cache->items, cache->nr + 1, cache->alloc);
 +      cache->items[cache->nr++] = obj;
 +}
 +
 +static void for_each_cached_alternate(void (*cb)(struct object *))
 +{
 +      static int initialized;
 +      static struct alternate_object_cache cache;
 +      size_t i;
 +
 +      if (!initialized) {
 +              for_each_alternate_ref(cache_one_alternate, &cache);
 +              initialized = 1;
 +      }
 +
 +      for (i = 0; i < cache.nr; i++)
 +              cb(cache.items[i]);
 +}
 +
  static void rev_list_push(struct commit *commit, int mark)
  {
        if (!(commit->object.flags & mark)) {
@@@ -235,7 -182,7 +235,7 @@@ enum ack_type 
  
  static void consume_shallow_list(struct fetch_pack_args *args, int fd)
  {
 -      if (args->stateless_rpc && args->depth > 0) {
 +      if (args->stateless_rpc && args->deepen) {
                /* If we sent a depth we will get back "duplicate"
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                                continue;
                        if (starts_with(line, "unshallow "))
                                continue;
 -                      die("git fetch-pack: expected shallow list");
 +                      die(_("git fetch-pack: expected shallow list"));
                }
        }
  }
@@@ -258,7 -205,7 +258,7 @@@ static enum ack_type get_ack(int fd, un
        const char *arg;
  
        if (!len)
 -              die("git fetch-pack: expected ACK/NAK, got EOF");
 +              die(_("git fetch-pack: expected ACK/NAK, got EOF"));
        if (!strcmp(line, "NAK"))
                return NAK;
        if (skip_prefix(line, "ACK ", &arg)) {
                        return ACK;
                }
        }
 -      die("git fetch_pack: expected ACK/NAK, got '%s'", line);
 +      if (skip_prefix(line, "ERR ", &arg))
 +              die(_("remote error: %s"), arg);
 +      die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
  }
  
  static void send_request(struct fetch_pack_args *args,
                write_or_die(fd, buf->buf, buf->len);
  }
  
 -static void insert_one_alternate_ref(const struct ref *ref, void *unused)
 +static void insert_one_alternate_object(struct object *obj)
  {
 -      rev_list_insert_ref(NULL, ref->old_oid.hash);
 +      rev_list_insert_ref(NULL, obj->oid.hash);
  }
  
  #define INITIAL_FLUSH 16
@@@ -330,13 -275,13 +330,13 @@@ static int find_common(struct fetch_pac
        size_t state_len = 0;
  
        if (args->stateless_rpc && multi_ack == 1)
 -              die("--stateless-rpc requires multi_ack_detailed");
 +              die(_("--stateless-rpc requires multi_ack_detailed"));
        if (marked)
                for_each_ref(clear_marks, NULL);
        marked = 1;
  
        for_each_ref(rev_list_insert_ref_oid, NULL);
 -      for_each_alternate_ref(insert_one_alternate_ref, NULL);
 +      for_each_cached_alternate(insert_one_alternate_object);
  
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
                        if (no_done)            strbuf_addstr(&c, " no-done");
                        if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
                        if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
 +                      if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
                        if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
                        if (args->no_progress)   strbuf_addstr(&c, " no-progress");
                        if (args->include_tag)   strbuf_addstr(&c, " include-tag");
                        if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
 +                      if (deepen_since_ok)    strbuf_addstr(&c, " deepen-since");
 +                      if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
                        if (agent_supported)    strbuf_addf(&c, " agent=%s",
                                                            git_user_agent_sanitized());
                        packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
                write_shallow_commits(&req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
 +      if (args->deepen_since) {
 +              unsigned long max_age = approxidate(args->deepen_since);
 +              packet_buf_write(&req_buf, "deepen-since %lu", max_age);
 +      }
 +      if (args->deepen_not) {
 +              int i;
 +              for (i = 0; i < args->deepen_not->nr; i++) {
 +                      struct string_list_item *s = args->deepen_not->items + i;
 +                      packet_buf_write(&req_buf, "deepen-not %s", s->string);
 +              }
 +      }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
  
 -      if (args->depth > 0) {
 +      if (args->deepen) {
                char *line;
                const char *arg;
                unsigned char sha1[20];
                while ((line = packet_read_line(fd[0], NULL))) {
                        if (skip_prefix(line, "shallow ", &arg)) {
                                if (get_sha1_hex(arg, sha1))
 -                                      die("invalid shallow line: %s", line);
 +                                      die(_("invalid shallow line: %s"), line);
                                register_shallow(sha1);
                                continue;
                        }
                        if (skip_prefix(line, "unshallow ", &arg)) {
                                if (get_sha1_hex(arg, sha1))
 -                                      die("invalid unshallow line: %s", line);
 +                                      die(_("invalid unshallow line: %s"), line);
                                if (!lookup_object(sha1))
 -                                      die("object not found: %s", line);
 +                                      die(_("object not found: %s"), line);
                                /* make sure that it is parsed as shallow */
                                if (!parse_object(sha1))
 -                                      die("error in object: %s", line);
 +                                      die(_("error in object: %s"), line);
                                if (unregister_shallow(sha1))
 -                                      die("no shallow found: %s", line);
 +                                      die(_("no shallow found: %s"), line);
                                continue;
                        }
 -                      die("expected shallow/unshallow, got %s", line);
 +                      die(_("expected shallow/unshallow, got %s"), line);
                }
        } else if (!args->stateless_rpc)
                send_request(args, fd[1], &req_buf);
        retval = -1;
        while ((sha1 = get_rev())) {
                packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
 -              if (args->verbose)
 -                      fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
 +              print_verbose(args, "have %s", sha1_to_hex(sha1));
                in_vain++;
                if (flush_at <= ++count) {
                        int ack;
                        consume_shallow_list(args, fd[0]);
                        do {
                                ack = get_ack(fd[0], result_sha1);
 -                              if (args->verbose && ack)
 -                                      fprintf(stderr, "got ack %d %s\n", ack,
 -                                                      sha1_to_hex(result_sha1));
 +                              if (ack)
 +                                      print_verbose(args, _("got %s %d %s"), "ack",
 +                                                    ack, sha1_to_hex(result_sha1));
                                switch (ack) {
                                case ACK:
                                        flushes = 0;
                                        struct commit *commit =
                                                lookup_commit(result_sha1);
                                        if (!commit)
 -                                              die("invalid commit %s", sha1_to_hex(result_sha1));
 +                                              die(_("invalid commit %s"), sha1_to_hex(result_sha1));
                                        if (args->stateless_rpc
                                         && ack == ACK_common
                                         && !(commit->object.flags & COMMON)) {
                        } while (ack);
                        flushes--;
                        if (got_continue && MAX_IN_VAIN < in_vain) {
 -                              if (args->verbose)
 -                                      fprintf(stderr, "giving up\n");
 +                              print_verbose(args, _("giving up"));
                                break; /* give up */
                        }
                }
@@@ -528,7 -461,8 +528,7 @@@ done
                packet_buf_write(&req_buf, "done\n");
                send_request(args, fd[1], &req_buf);
        }
 -      if (args->verbose)
 -              fprintf(stderr, "done\n");
 +      print_verbose(args, _("done"));
        if (retval != 0) {
                multi_ack = 0;
                flushes++;
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
 -                      if (args->verbose)
 -                              fprintf(stderr, "got ack (%d) %s\n", ack,
 -                                      sha1_to_hex(result_sha1));
 +                      print_verbose(args, _("got %s (%d) %s"), "ack",
 +                                    ack, sha1_to_hex(result_sha1));
                        if (ack == ACK)
                                return 0;
                        multi_ack = 1;
@@@ -586,8 -521,9 +586,8 @@@ static void mark_recent_complete_commit
                                         unsigned long cutoff)
  {
        while (complete && cutoff <= complete->item->date) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Marking %s as complete\n",
 -                              oid_to_hex(&complete->item->object.oid));
 +              print_verbose(args, _("Marking %s as complete"),
 +                            oid_to_hex(&complete->item->object.oid));
                pop_most_recent_commit(&complete, COMPLETE);
        }
  }
@@@ -616,14 -552,14 +616,14 @@@ static void filter_refs(struct fetch_pa
                                        break; /* definitely do not have it */
                                else if (cmp == 0) {
                                        keep = 1; /* definitely have it */
 -                                      sought[i]->matched = 1;
 +                                      sought[i]->match_status = REF_MATCHED;
                                }
                                i++;
                        }
                }
  
                if (!keep && args->fetch_all &&
 -                  (!args->depth || !starts_with(ref->name, "refs/tags/")))
 +                  (!args->deepen || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
  
                if (keep) {
        }
  
        /* Append unmatched requests to the list */
 -      if ((allow_unadvertised_object_request &
 -          (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
 -              for (i = 0; i < nr_sought; i++) {
 -                      unsigned char sha1[20];
 +      for (i = 0; i < nr_sought; i++) {
 +              unsigned char sha1[20];
  
 -                      ref = sought[i];
 -                      if (ref->matched)
 -                              continue;
 -                      if (get_sha1_hex(ref->name, sha1) ||
 -                          ref->name[40] != '\0' ||
 -                          hashcmp(sha1, ref->old_oid.hash))
 -                              continue;
 +              ref = sought[i];
 +              if (ref->match_status != REF_NOT_MATCHED)
 +                      continue;
 +              if (get_sha1_hex(ref->name, sha1) ||
 +                  ref->name[40] != '\0' ||
 +                  hashcmp(sha1, ref->old_oid.hash))
 +                      continue;
  
 -                      ref->matched = 1;
 +              if ((allow_unadvertised_object_request &
 +                  (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
 +                      ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
 +              } else {
 +                      ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
                }
        }
        *refs = newlist;
  }
  
 -static void mark_alternate_complete(const struct ref *ref, void *unused)
 +static void mark_alternate_complete(struct object *obj)
  {
 -      mark_complete(ref->old_oid.hash);
 +      mark_complete(obj->oid.hash);
  }
  
  static int everything_local(struct fetch_pack_args *args,
                }
        }
  
 -      if (!args->depth) {
 +      if (!args->deepen) {
                for_each_ref(mark_complete_oid, NULL);
 -              for_each_alternate_ref(mark_alternate_complete, NULL);
 +              for_each_cached_alternate(mark_alternate_complete);
                commit_list_sort_by_date(&complete);
                if (cutoff)
                        mark_recent_complete_commits(args, cutoff);
                o = lookup_object(remote);
                if (!o || !(o->flags & COMPLETE)) {
                        retval = 0;
 -                      if (!args->verbose)
 -                              continue;
 -                      fprintf(stderr,
 -                              "want %s (%s)\n", sha1_to_hex(remote),
 -                              ref->name);
 +                      print_verbose(args, "want %s (%s)", sha1_to_hex(remote),
 +                                    ref->name);
                        continue;
                }
 -              if (!args->verbose)
 -                      continue;
 -              fprintf(stderr,
 -                      "already have %s (%s)\n", sha1_to_hex(remote),
 -                      ref->name);
 +              print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote),
 +                            ref->name);
        }
        return retval;
  }
@@@ -772,7 -712,8 +772,7 @@@ static int get_pack(struct fetch_pack_a
                demux.out = -1;
                demux.isolate_sigpipe = 1;
                if (start_async(&demux))
 -                      die("fetch-pack: unable to fork off sideband"
 -                          " demultiplexer");
 +                      die(_("fetch-pack: unable to fork off sideband demultiplexer"));
        }
        else
                demux.out = xd[0];
        if (!args->keep_pack && unpack_limit) {
  
                if (read_pack_header(demux.out, &header))
 -                      die("protocol error: bad pack header");
 +                      die(_("protocol error: bad pack header"));
                pass_header = 1;
                if (ntohl(header.hdr_entries) < unpack_limit)
                        do_keep = 0;
                if (args->use_thin_pack)
                        argv_array_push(&cmd.args, "--fix-thin");
                if (args->lock_pack || unpack_limit) {
-                       char hostname[256];
-                       if (gethostname(hostname, sizeof(hostname)))
+                       char hostname[HOST_NAME_MAX + 1];
+                       if (xgethostname(hostname, sizeof(hostname)))
                                xsnprintf(hostname, sizeof(hostname), "localhost");
                        argv_array_pushf(&cmd.args,
                                        "--keep=fetch-pack %"PRIuMAX " on %s",
        cmd.in = demux.out;
        cmd.git_cmd = 1;
        if (start_command(&cmd))
 -              die("fetch-pack: unable to fork off %s", cmd_name);
 +              die(_("fetch-pack: unable to fork off %s"), cmd_name);
        if (do_keep && pack_lockfile) {
                *pack_lockfile = index_pack_lockfile(cmd.out);
                close(cmd.out);
                        args->check_self_contained_and_connected &&
                        ret == 0;
        else
 -              die("%s failed", cmd_name);
 +              die(_("%s failed"), cmd_name);
        if (use_sideband && finish_async(&demux))
 -              die("error in sideband demultiplexer");
 +              die(_("error in sideband demultiplexer"));
        return 0;
  }
  
@@@ -878,39 -819,44 +878,39 @@@ static struct ref *do_fetch_pack(struc
        int agent_len;
  
        sort_ref_list(&ref, ref_compare_name);
 -      qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
 +      QSORT(sought, nr_sought, cmp_ref_by_name);
  
        if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
 -              die("Server does not support shallow clients");
 +              die(_("Server does not support shallow clients"));
 +      if (args->depth > 0 || args->deepen_since || args->deepen_not)
 +              args->deepen = 1;
        if (server_supports("multi_ack_detailed")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports multi_ack_detailed\n");
 +              print_verbose(args, _("Server supports multi_ack_detailed"));
                multi_ack = 2;
                if (server_supports("no-done")) {
 -                      if (args->verbose)
 -                              fprintf(stderr, "Server supports no-done\n");
 +                      print_verbose(args, _("Server supports no-done"));
                        if (args->stateless_rpc)
                                no_done = 1;
                }
        }
        else if (server_supports("multi_ack")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports multi_ack\n");
 +              print_verbose(args, _("Server supports multi_ack"));
                multi_ack = 1;
        }
        if (server_supports("side-band-64k")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports side-band-64k\n");
 +              print_verbose(args, _("Server supports side-band-64k"));
                use_sideband = 2;
        }
        else if (server_supports("side-band")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports side-band\n");
 +              print_verbose(args, _("Server supports side-band"));
                use_sideband = 1;
        }
        if (server_supports("allow-tip-sha1-in-want")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
 +              print_verbose(args, _("Server supports allow-tip-sha1-in-want"));
                allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
        }
        if (server_supports("allow-reachable-sha1-in-want")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
 +              print_verbose(args, _("Server supports allow-reachable-sha1-in-want"));
                allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
        }
        if (!server_supports("thin-pack"))
                args->no_progress = 0;
        if (!server_supports("include-tag"))
                args->include_tag = 0;
 -      if (server_supports("ofs-delta")) {
 -              if (args->verbose)
 -                      fprintf(stderr, "Server supports ofs-delta\n");
 -      } else
 +      if (server_supports("ofs-delta"))
 +              print_verbose(args, _("Server supports ofs-delta"));
 +      else
                prefer_ofs_delta = 0;
  
        if ((agent_feature = server_feature_value("agent", &agent_len))) {
                agent_supported = 1;
 -              if (args->verbose && agent_len)
 -                      fprintf(stderr, "Server version is %.*s\n",
 -                              agent_len, agent_feature);
 +              if (agent_len)
 +                      print_verbose(args, _("Server version is %.*s"),
 +                                    agent_len, agent_feature);
        }
 +      if (server_supports("deepen-since"))
 +              deepen_since_ok = 1;
 +      else if (args->deepen_since)
 +              die(_("Server does not support --shallow-since"));
 +      if (server_supports("deepen-not"))
 +              deepen_not_ok = 1;
 +      else if (args->deepen_not)
 +              die(_("Server does not support --shallow-exclude"));
 +      if (!server_supports("deepen-relative") && args->deepen_relative)
 +              die(_("Server does not support --deepen"));
  
        if (everything_local(args, &ref, sought, nr_sought)) {
                packet_flush(fd[1]);
                        /* When cloning, it is not unusual to have
                         * no common commit.
                         */
 -                      warning("no common commits");
 +                      warning(_("no common commits"));
  
        if (args->stateless_rpc)
                packet_flush(fd[1]);
 -      if (args->depth > 0)
 +      if (args->deepen)
                setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
                                        NULL);
        else if (si->nr_ours || si->nr_theirs)
        else
                alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
 -              die("git fetch-pack: fetch failed.");
 +              die(_("git fetch-pack: fetch failed."));
  
   all_done:
        return ref;
@@@ -1017,11 -954,11 +1017,11 @@@ static void update_shallow(struct fetch
                           struct ref **sought, int nr_sought,
                           struct shallow_info *si)
  {
 -      struct sha1_array ref = SHA1_ARRAY_INIT;
 +      struct oid_array ref = OID_ARRAY_INIT;
        int *status;
        int i;
  
 -      if (args->depth > 0 && alternate_shallow_file) {
 +      if (args->deepen && alternate_shallow_file) {
                if (*alternate_shallow_file == '\0') { /* --unshallow */
                        unlink_or_warn(git_path_shallow());
                        rollback_lock_file(&shallow_lock);
                 * shallow points that exist in the pack (iow in repo
                 * after get_pack() and reprepare_packed_git())
                 */
 -              struct sha1_array extra = SHA1_ARRAY_INIT;
 -              unsigned char (*sha1)[20] = si->shallow->sha1;
 +              struct oid_array extra = OID_ARRAY_INIT;
 +              struct object_id *oid = si->shallow->oid;
                for (i = 0; i < si->shallow->nr; i++)
 -                      if (has_sha1_file(sha1[i]))
 -                              sha1_array_append(&extra, sha1[i]);
 +                      if (has_object_file(&oid[i]))
 +                              oid_array_append(&extra, &oid[i]);
                if (extra.nr) {
                        setup_alternate_shallow(&shallow_lock,
                                                &alternate_shallow_file,
                                                &extra);
                        commit_lock_file(&shallow_lock);
                }
 -              sha1_array_clear(&extra);
 +              oid_array_clear(&extra);
                return;
        }
  
        if (!si->nr_ours && !si->nr_theirs)
                return;
        for (i = 0; i < nr_sought; i++)
 -              sha1_array_append(&ref, sought[i]->old_oid.hash);
 +              oid_array_append(&ref, &sought[i]->old_oid);
        si->ref = &ref;
  
        if (args->update_shallow) {
                 * shallow roots that are actually reachable from new
                 * refs.
                 */
 -              struct sha1_array extra = SHA1_ARRAY_INIT;
 -              unsigned char (*sha1)[20] = si->shallow->sha1;
 +              struct oid_array extra = OID_ARRAY_INIT;
 +              struct object_id *oid = si->shallow->oid;
                assign_shallow_commits_to_refs(si, NULL, NULL);
                if (!si->nr_ours && !si->nr_theirs) {
 -                      sha1_array_clear(&ref);
 +                      oid_array_clear(&ref);
                        return;
                }
                for (i = 0; i < si->nr_ours; i++)
 -                      sha1_array_append(&extra, sha1[si->ours[i]]);
 +                      oid_array_append(&extra, &oid[si->ours[i]]);
                for (i = 0; i < si->nr_theirs; i++)
 -                      sha1_array_append(&extra, sha1[si->theirs[i]]);
 +                      oid_array_append(&extra, &oid[si->theirs[i]]);
                setup_alternate_shallow(&shallow_lock,
                                        &alternate_shallow_file,
                                        &extra);
                commit_lock_file(&shallow_lock);
 -              sha1_array_clear(&extra);
 -              sha1_array_clear(&ref);
 +              oid_array_clear(&extra);
 +              oid_array_clear(&ref);
                return;
        }
  
                                sought[i]->status = REF_STATUS_REJECT_SHALLOW;
        }
        free(status);
 -      sha1_array_clear(&ref);
 +      oid_array_clear(&ref);
  }
  
  struct ref *fetch_pack(struct fetch_pack_args *args,
                       const struct ref *ref,
                       const char *dest,
                       struct ref **sought, int nr_sought,
 -                     struct sha1_array *shallow,
 +                     struct oid_array *shallow,
                       char **pack_lockfile)
  {
        struct ref *ref_cpy;
  
        if (!ref) {
                packet_flush(fd[1]);
 -              die("no matching remote head");
 +              die(_("no matching remote head"));
        }
        prepare_shallow_info(&si, shallow);
        ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
        clear_shallow_info(&si);
        return ref_cpy;
  }
 +
 +int report_unmatched_refs(struct ref **sought, int nr_sought)
 +{
 +      int i, ret = 0;
 +
 +      for (i = 0; i < nr_sought; i++) {
 +              if (!sought[i])
 +                      continue;
 +              switch (sought[i]->match_status) {
 +              case REF_MATCHED:
 +                      continue;
 +              case REF_NOT_MATCHED:
 +                      error(_("no such remote ref %s"), sought[i]->name);
 +                      break;
 +              case REF_UNADVERTISED_NOT_ALLOWED:
 +                      error(_("Server does not allow request for unadvertised object %s"),
 +                            sought[i]->name);
 +                      break;
 +              }
 +              ret = 1;
 +      }
 +      return ret;
 +}
diff --combined git-compat-util.h
index 8a4a3f85e7ba0a658420c477e1af82e804d7fd36,b8c4c8d638dfc9cdb261db3e64ab3eb33f3b5558..bd04564a69a052ce856f2ba120855541c66fc650
@@@ -441,9 -441,6 +441,9 @@@ static inline int const_error(void
  
  extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
  extern void set_error_routine(void (*routine)(const char *err, va_list params));
 +extern void (*get_error_routine(void))(const char *err, va_list params);
 +extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
 +extern void (*get_warn_routine(void))(const char *warn, va_list params);
  extern void set_die_is_recursing_routine(int (*routine)(void));
  extern void set_error_handle(FILE *);
  
@@@ -527,16 -524,6 +527,16 @@@ static inline int ends_with(const char 
        return strip_suffix(str, suffix, &len);
  }
  
 +#define SWAP(a, b) do {                                               \
 +      void *_swap_a_ptr = &(a);                               \
 +      void *_swap_b_ptr = &(b);                               \
 +      unsigned char _swap_buffer[sizeof(a)];                  \
 +      memcpy(_swap_buffer, _swap_a_ptr, sizeof(a));           \
 +      memcpy(_swap_a_ptr, _swap_b_ptr, sizeof(a) +            \
 +             BUILD_ASSERT_OR_ZERO(sizeof(a) == sizeof(b)));   \
 +      memcpy(_swap_b_ptr, _swap_buffer, sizeof(a));           \
 +} while (0)
 +
  #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
  
  #ifndef PROT_READ
@@@ -639,6 -626,11 +639,6 @@@ extern int gitsetenv(const char *, cons
  extern char *gitmkdtemp(char *);
  #endif
  
 -#ifdef NO_MKSTEMPS
 -#define mkstemps gitmkstemps
 -extern int gitmkstemps(char *, int);
 -#endif
 -
  #ifdef NO_UNSETENV
  #define unsetenv gitunsetenv
  extern void gitunsetenv(const char *);
@@@ -798,6 -790,8 +798,6 @@@ extern FILE *xfopen(const char *path, c
  extern FILE *xfdopen(int fd, const char *mode);
  extern int xmkstemp(char *template);
  extern int xmkstemp_mode(char *template, int mode);
 -extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 -extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
  extern char *xgetcwd(void);
  extern FILE *fopen_for_writing(const char *path);
  
@@@ -884,6 -878,12 +884,12 @@@ static inline size_t xsize_t(off_t len
  __attribute__((format (printf, 3, 4)))
  extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
  
+ #ifndef HOST_NAME_MAX
+ #define HOST_NAME_MAX 256
+ #endif
+ extern int xgethostname(char *buf, size_t len);
  /* in ctype.c, for kwset users */
  extern const unsigned char tolower_trans_tbl[256];
  
@@@ -983,25 -983,6 +989,25 @@@ void git_qsort(void *base, size_t nmemb
  #define qsort git_qsort
  #endif
  
 +#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
 +static inline void sane_qsort(void *base, size_t nmemb, size_t size,
 +                            int(*compar)(const void *, const void *))
 +{
 +      if (nmemb > 1)
 +              qsort(base, nmemb, size, compar);
 +}
 +
 +#ifndef HAVE_ISO_QSORT_S
 +int git_qsort_s(void *base, size_t nmemb, size_t size,
 +              int (*compar)(const void *, const void *, void *), void *ctx);
 +#define qsort_s git_qsort_s
 +#endif
 +
 +#define QSORT_S(base, n, compar, ctx) do {                    \
 +      if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \
 +              die("BUG: qsort_s() failed");                   \
 +} while (0)
 +
  #ifndef REG_STARTEND
  #error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
  #endif
diff --combined ident.c
index c0364fe3a1630086e0e3752fe3e7e51e2027ea44,7e386a48fa8331e69fae2a7b6a86b770e370cec5..bea871c8e02b7173eeba5527d22f5ae7783c011e
+++ b/ident.c
@@@ -120,9 -120,9 +120,9 @@@ static int canonical_name(const char *h
  
  static void add_domainname(struct strbuf *out, int *is_bogus)
  {
-       char buf[1024];
+       char buf[HOST_NAME_MAX + 1];
  
-       if (gethostname(buf, sizeof(buf))) {
+       if (xgethostname(buf, sizeof(buf))) {
                warning_errno("cannot get host name");
                strbuf_addstr(out, "(none)");
                *is_bogus = 1;
@@@ -153,7 -153,7 +153,7 @@@ static void copy_email(const struct pas
  
  const char *ident_default_name(void)
  {
 -      if (!git_default_name.len) {
 +      if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) {
                copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name);
                strbuf_trim(&git_default_name);
        }
  
  const char *ident_default_email(void)
  {
 -      if (!git_default_email.len) {
 +      if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) {
                const char *email = getenv("EMAIL");
  
                if (email && email[0]) {
@@@ -203,15 -203,6 +203,15 @@@ static int crud(unsigned char c
                c == '\'';
  }
  
 +static int has_non_crud(const char *str)
 +{
 +      for (; *str; str++) {
 +              if (!crud(*str))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
  /*
   * Copy over a string to the destination, but avoid special
   * characters ('\n', '<' and '>') and remove crud at the end
@@@ -340,17 -331,17 +340,17 @@@ person_only
  }
  
  static const char *env_hint =
 -"\n"
 -"*** Please tell me who you are.\n"
 -"\n"
 -"Run\n"
 -"\n"
 -"  git config --global user.email \"you@example.com\"\n"
 -"  git config --global user.name \"Your Name\"\n"
 -"\n"
 -"to set your account\'s default identity.\n"
 -"Omit --global to set the identity only in this repository.\n"
 -"\n";
 +N_("\n"
 +   "*** Please tell me who you are.\n"
 +   "\n"
 +   "Run\n"
 +   "\n"
 +   "  git config --global user.email \"you@example.com\"\n"
 +   "  git config --global user.name \"Your Name\"\n"
 +   "\n"
 +   "to set your account\'s default identity.\n"
 +   "Omit --global to set the identity only in this repository.\n"
 +   "\n");
  
  const char *fmt_ident(const char *name, const char *email,
                      const char *date_str, int flag)
        int want_date = !(flag & IDENT_NO_DATE);
        int want_name = !(flag & IDENT_NO_NAME);
  
 +      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 (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");
 +                              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);
 +                              fputs(_(env_hint), stderr);
 +                              die(_("unable to auto-detect name (got '%s')"), name);
                        }
                }
                if (!*name) {
                        struct passwd *pw;
                        if (strict) {
                                if (using_default)
 -                                      fputs(env_hint, stderr);
 -                              die("empty ident name (for <%s>) not allowed", email);
 +                                      fputs(_(env_hint), stderr);
 +                              die(_("empty ident name (for <%s>) not allowed"), email);
                        }
                        pw = xgetpwuid_self(NULL);
                        name = pw->pw_name;
                }
 -      }
 -
 -      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 && !has_non_crud(name))
 +                      die(_("name consists only of disallowed characters: %s"), name);
        }
  
        strbuf_reset(&ident);
                strbuf_addch(&ident, ' ');
                if (date_str && date_str[0]) {
                        if (parse_date(date_str, &ident) < 0)
 -                              die("invalid date format: %s", date_str);
 +                              die(_("invalid date format: %s"), date_str);
                }
                else
                        strbuf_addstr(&ident, ident_default_date());
diff --combined wrapper.c
index 0542fc75821fdbdd7bfecebec5f7df01e49dcb0c,64ebd6c690d70203578c0d928a1f1886ad2a6b0e..d83741770949f457b364896b6ff8632c0a700d69
+++ b/wrapper.c
@@@ -440,6 -440,23 +440,6 @@@ int xmkstemp(char *template
        return fd;
  }
  
 -/* git_mkstemp() - create tmp file honoring TMPDIR variable */
 -int git_mkstemp(char *path, size_t len, const char *template)
 -{
 -      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 mkstemp(path);
 -}
 -
  /* Adapted from libiberty's mkstemp.c. */
  
  #undef TMP_MAX
@@@ -514,6 -531,13 +514,6 @@@ int git_mkstemp_mode(char *pattern, in
        return git_mkstemps_mode(pattern, 0, mode);
  }
  
 -#ifdef NO_MKSTEMPS
 -int gitmkstemps(char *pattern, int suffix_len)
 -{
 -      return git_mkstemps_mode(pattern, suffix_len, 0600);
 -}
 -#endif
 -
  int xmkstemp_mode(char *template, int mode)
  {
        int fd;
@@@ -655,3 -679,16 +655,16 @@@ void sleep_millisec(int millisec
  {
        poll(NULL, 0, millisec);
  }
+ int xgethostname(char *buf, size_t len)
+ {
+       /*
+        * If the full hostname doesn't fit in buf, POSIX does not
+        * specify whether the buffer will be null-terminated, so to
+        * be safe, do it ourselves.
+        */
+       int ret = gethostname(buf, len);
+       if (!ret)
+               buf[len - 1] = 0;
+       return ret;
+ }