]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Sync with 2.19.3
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 4 Dec 2019 21:31:10 +0000 (22:31 +0100)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 6 Dec 2019 15:30:49 +0000 (16:30 +0100)
* maint-2.19: (34 commits)
  Git 2.19.3
  Git 2.18.2
  Git 2.17.3
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  ...

23 files changed:
1  2 
builtin/clone.c
builtin/submodule--helper.c
compat/mingw.c
compat/mingw.h
config.mak.uname
connect.c
environment.c
fast-import.c
fsck.c
git-compat-util.h
git-submodule.sh
path.c
read-cache.c
submodule-config.c
submodule.c
submodule.h
t/t0060-path-utils.sh
t/t1450-fsck.sh
t/t7406-submodule-update.sh
t/t9300-fast-import.sh
transport-helper.c
tree-walk.c
unpack-trees.c

diff --combined builtin/clone.c
index 15b142d64640e29c10e62d565ac21adbaaeebca4,4843c577236fa0ac47337eed884f1213c0e4bb84..5d31bea55e0e0f22012c96757eb9106c9d1e5133
@@@ -748,7 -748,6 +748,7 @@@ static int checkout(int submodule_progr
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
        opts.merge = 1;
 +      opts.clone = 1;
        opts.fn = oneway_merge;
        opts.verbose_update = (option_verbosity >= 0);
        opts.src_index = &the_index;
  
        if (!err && (option_recurse_submodules.nr > 0)) {
                struct argv_array args = ARGV_ARRAY_INIT;
-               argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+               argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
  
                if (option_shallow_submodules == 1)
                        argv_array_push(&args, "--depth=1");
index d38113a31aeb3838190b7339475c7454e45f3a89,382e823b3c2ae3db63f017625ef65be1f0291100..b93d624a859c2e321f92adf7676451011e662465
@@@ -18,6 -18,7 +18,7 @@@
  #include "diffcore.h"
  #include "diff.h"
  #include "object-store.h"
+ #include "dir.h"
  
  #define OPT_QUIET (1 << 0)
  #define OPT_CACHED (1 << 1)
@@@ -584,26 -585,6 +585,26 @@@ static int module_foreach(int argc, con
        return 0;
  }
  
 +static char *compute_submodule_clone_url(const char *rel_url)
 +{
 +      char *remoteurl, *relurl;
 +      char *remote = get_default_remote();
 +      struct strbuf remotesb = STRBUF_INIT;
 +
 +      strbuf_addf(&remotesb, "remote.%s.url", remote);
 +      if (git_config_get_string(remotesb.buf, &remoteurl)) {
 +              warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
 +              remoteurl = xgetcwd();
 +      }
 +      relurl = relative_url(remoteurl, rel_url, NULL);
 +
 +      free(remote);
 +      free(remoteurl);
 +      strbuf_release(&remotesb);
 +
 +      return relurl;
 +}
 +
  struct init_cb {
        const char *prefix;
        unsigned int flags;
@@@ -654,9 -635,21 +655,9 @@@ static void init_submodule(const char *
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
 -                      char *remoteurl, *relurl;
 -                      char *remote = get_default_remote();
 -                      struct strbuf remotesb = STRBUF_INIT;
 -                      strbuf_addf(&remotesb, "remote.%s.url", remote);
 -                      free(remote);
 -
 -                      if (git_config_get_string(remotesb.buf, &remoteurl)) {
 -                              warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
 -                              remoteurl = xgetcwd();
 -                      }
 -                      relurl = relative_url(remoteurl, url, NULL);
 -                      strbuf_release(&remotesb);
 -                      free(remoteurl);
 -                      free(url);
 -                      url = relurl;
 +                      char *oldurl = url;
 +                      url = compute_submodule_clone_url(oldurl);
 +                      free(oldurl);
                }
  
                if (git_config_set_gently(sb.buf, url))
@@@ -800,7 -793,7 +801,7 @@@ static void status_submodule(const cha
                         path, NULL);
  
        git_config(git_diff_basic_config, NULL);
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        diff_files_args.argc = setup_revisions(diff_files_args.argc,
                                               diff_files_args.argv,
@@@ -1354,7 -1347,7 +1355,7 @@@ static int module_clone(int argc, cons
        char *p, *path = NULL, *sm_gitdir;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
-       int dissociate = 0;
+       int dissociate = 0, require_init = 0;
        char *sm_alternate = NULL, *error_strategy = NULL;
  
        struct option module_clone_options[] = {
                OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
                OPT_BOOL(0, "progress", &progress,
                           N_("force cloning progress")),
+               OPT_BOOL(0, "require-init", &require_init,
+                          N_("disallow cloning into non-empty directory")),
                OPT_END()
        };
  
        } else
                path = xstrdup(path);
  
+       if (validate_submodule_git_dir(sm_gitdir, name) < 0)
+               die(_("refusing to create/use '%s' in another submodule's "
+                       "git dir"), sm_gitdir);
        if (!file_exists(sm_gitdir)) {
                if (safe_create_leading_directories_const(sm_gitdir) < 0)
                        die(_("could not create directory '%s'"), sm_gitdir);
                        die(_("clone of '%s' into submodule path '%s' failed"),
                            url, path);
        } else {
+               if (require_init && !access(path, X_OK) && !is_empty_dir(path))
+                       die(_("directory not empty: '%s'"), path);
                if (safe_create_leading_directories_const(path) < 0)
                        die(_("could not create directory '%s'"), path);
                strbuf_addf(&sb, "%s/index", sm_gitdir);
        return 0;
  }
  
 +static void determine_submodule_update_strategy(struct repository *r,
 +                                              int just_cloned,
 +                                              const char *path,
 +                                              const char *update,
 +                                              struct submodule_update_strategy *out)
 +{
 +      const struct submodule *sub = submodule_from_path(r, &null_oid, path);
 +      char *key;
 +      const char *val;
 +
 +      key = xstrfmt("submodule.%s.update", sub->name);
 +
 +      if (update) {
 +              if (parse_submodule_update_strategy(update, out) < 0)
 +                      die(_("Invalid update mode '%s' for submodule path '%s'"),
 +                              update, path);
 +      } else if (!repo_config_get_string_const(r, key, &val)) {
 +              if (parse_submodule_update_strategy(val, out) < 0)
 +                      die(_("Invalid update mode '%s' configured for submodule path '%s'"),
 +                              val, path);
 +      } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
 +              out->type = sub->update_strategy.type;
 +              out->command = sub->update_strategy.command;
 +      } else
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      if (just_cloned &&
 +          (out->type == SM_UPDATE_MERGE ||
 +           out->type == SM_UPDATE_REBASE ||
 +           out->type == SM_UPDATE_NONE))
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      free(key);
 +}
 +
 +static int module_update_module_mode(int argc, const char **argv, const char *prefix)
 +{
 +      const char *path, *update = NULL;
 +      int just_cloned;
 +      struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
 +
 +      if (argc < 3 || argc > 4)
 +              die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
 +
 +      just_cloned = git_config_int("just_cloned", argv[1]);
 +      path = argv[2];
 +
 +      if (argc == 4)
 +              update = argv[3];
 +
 +      determine_submodule_update_strategy(the_repository,
 +                                          just_cloned, path, update,
 +                                          &update_strategy);
 +      fputs(submodule_strategy_to_string(&update_strategy), stdout);
 +
 +      return 0;
 +}
 +
 +struct update_clone_data {
 +      const struct submodule *sub;
 +      struct object_id oid;
 +      unsigned just_cloned;
 +};
 +
  struct submodule_update_clone {
        /* index into 'list', the list of submodules to look into for cloning */
        int current;
        int recommend_shallow;
        struct string_list references;
        int dissociate;
+       unsigned require_init;
        const char *depth;
        const char *recursive_prefix;
        const char *prefix;
  
 -      /* Machine-readable status lines to be consumed by git-submodule.sh */
 -      struct string_list projectlines;
 +      /* to be consumed by git-submodule.sh */
 +      struct update_clone_data *update_clone;
 +      int update_clone_nr; int update_clone_alloc;
  
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
        /* failed clones to be retried again */
        const struct cache_entry **failed_clones;
        int failed_clones_nr, failed_clones_alloc;
 +
 +      int max_jobs;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
-       SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
+       SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
        NULL, NULL, NULL, \
 -      STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 +      NULL, 0, 0, 0, NULL, 0, 0, 0}
  
  
  static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@@ -1590,7 -1525,6 +1600,7 @@@ static int prepare_to_clone_next_submod
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
        int needs_cloning = 0;
 +      int need_free_url = 0;
  
        if (ce_stage(ce)) {
                if (suc->recursive_prefix)
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
 -      if (repo_config_get_string_const(the_repository, sb.buf, &url))
 -              url = sub->url;
 +      if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
 +              if (starts_with_dot_slash(sub->url) ||
 +                  starts_with_dot_dot_slash(sub->url)) {
 +                      url = compute_submodule_clone_url(sub->url);
 +                      need_free_url = 1;
 +              } else
 +                      url = sub->url;
 +      }
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
  
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
 -                      oid_to_hex(&ce->oid), ce_stage(ce),
 -                      needs_cloning, ce->name);
 -      string_list_append(&suc->projectlines, sb.buf);
 +      ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
 +                 suc->update_clone_alloc);
 +      oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
 +      suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
 +      suc->update_clone[suc->update_clone_nr].sub = sub;
 +      suc->update_clone_nr++;
  
        if (!needs_cloning)
                goto cleanup;
                argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
        if (suc->recommend_shallow && sub->recommend_shallow == 1)
                argv_array_push(&child->args, "--depth=1");
+       if (suc->require_init)
+               argv_array_push(&child->args, "--require-init");
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
        argv_array_pushl(&child->args, "--url", url, NULL);
  cleanup:
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
 +      if (need_free_url)
 +              free((void*)url);
  
        return needs_cloning;
  }
@@@ -1800,44 -1727,11 +1812,44 @@@ static int git_update_clone_config(cons
        return 0;
  }
  
 +static void update_submodule(struct update_clone_data *ucd)
 +{
 +      fprintf(stdout, "dummy %s %d\t%s\n",
 +              oid_to_hex(&ucd->oid),
 +              ucd->just_cloned,
 +              ucd->sub->path);
 +}
 +
 +static int update_submodules(struct submodule_update_clone *suc)
 +{
 +      int i;
 +
 +      run_processes_parallel(suc->max_jobs,
 +                             update_clone_get_next_task,
 +                             update_clone_start_failure,
 +                             update_clone_task_finished,
 +                             suc);
 +
 +      /*
 +       * We saved the output and put it out all at once now.
 +       * That means:
 +       * - the listener does not have to interleave their (checkout)
 +       *   work with our fetching.  The writes involved in a
 +       *   checkout involve more straightforward sequential I/O.
 +       * - the listener can avoid doing any work if fetching failed.
 +       */
 +      if (suc->quickstop)
 +              return 1;
 +
 +      for (i = 0; i < suc->update_clone_nr; i++)
 +              update_submodule(&suc->update_clone[i]);
 +
 +      return 0;
 +}
 +
  static int update_clone(int argc, const char **argv, const char *prefix)
  {
        const char *update = NULL;
 -      int max_jobs = 1;
 -      struct string_list_item *item;
        struct pathspec pathspec;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
  
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                              "specified number of revisions")),
 -              OPT_INTEGER('j', "jobs", &max_jobs,
 +              OPT_INTEGER('j', "jobs", &suc.max_jobs,
                            N_("parallel jobs")),
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
                OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
                OPT_BOOL(0, "progress", &suc.progress,
                            N_("force cloning progress")),
+               OPT_BOOL(0, "require-init", &suc.require_init,
+                          N_("disallow cloning into non-empty directory")),
                OPT_END()
        };
  
        };
        suc.prefix = prefix;
  
 -      update_clone_config_from_gitmodules(&max_jobs);
 -      git_config(git_update_clone_config, &max_jobs);
 +      update_clone_config_from_gitmodules(&suc.max_jobs);
 +      git_config(git_update_clone_config, &suc.max_jobs);
  
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
  
 -      run_processes_parallel(max_jobs,
 -                             update_clone_get_next_task,
 -                             update_clone_start_failure,
 -                             update_clone_task_finished,
 -                             &suc);
 -
 -      /*
 -       * We saved the output and put it out all at once now.
 -       * That means:
 -       * - the listener does not have to interleave their (checkout)
 -       *   work with our fetching.  The writes involved in a
 -       *   checkout involve more straightforward sequential I/O.
 -       * - the listener can avoid doing any work if fetching failed.
 -       */
 -      if (suc.quickstop)
 -              return 1;
 -
 -      for_each_string_list_item(item, &suc.projectlines)
 -              fprintf(stdout, "%s", item->string);
 -
 -      return 0;
 +      return update_submodules(&suc);
  }
  
  static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@@ -2037,45 -1953,6 +2051,45 @@@ static int push_check(int argc, const c
        return 0;
  }
  
 +static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
 +{
 +      const struct submodule *sub;
 +      const char *path;
 +      char *cw;
 +      struct repository subrepo;
 +
 +      if (argc != 2)
 +              BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
 +
 +      path = argv[1];
 +
 +      sub = submodule_from_path(the_repository, &null_oid, path);
 +      if (!sub)
 +              BUG("We could get the submodule handle before?");
 +
 +      if (repo_submodule_init(&subrepo, the_repository, path))
 +              die(_("could not get a repository handle for submodule '%s'"), path);
 +
 +      if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
 +              char *cfg_file, *abs_path;
 +              const char *rel_path;
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              cfg_file = repo_git_path(&subrepo, "config");
 +
 +              abs_path = absolute_pathdup(path);
 +              rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
 +
 +              git_config_set_in_file(cfg_file, "core.worktree", rel_path);
 +
 +              free(cfg_file);
 +              free(abs_path);
 +              strbuf_release(&sb);
 +      }
 +
 +      return 0;
 +}
 +
  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
  {
        int i;
@@@ -2141,45 -2018,6 +2155,45 @@@ static int check_name(int argc, const c
        return 0;
  }
  
 +static int module_config(int argc, const char **argv, const char *prefix)
 +{
 +      enum {
 +              CHECK_WRITEABLE = 1
 +      } command = 0;
 +
 +      struct option module_config_options[] = {
 +              OPT_CMDMODE(0, "check-writeable", &command,
 +                          N_("check if it is safe to write to the .gitmodules file"),
 +                          CHECK_WRITEABLE),
 +              OPT_END()
 +      };
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper config name [value]"),
 +              N_("git submodule--helper config --check-writeable"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_config_options,
 +                           git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
 +
 +      if (argc == 1 && command == CHECK_WRITEABLE)
 +              return is_writing_gitmodules_ok() ? 0 : -1;
 +
 +      /* Equivalent to ACTION_GET in builtin/config.c */
 +      if (argc == 2)
 +              return print_config_from_gitmodules(the_repository, argv[1]);
 +
 +      /* Equivalent to ACTION_SET in builtin/config.c */
 +      if (argc == 3) {
 +              if (!is_writing_gitmodules_ok())
 +                      die(_("please make sure that the .gitmodules file is in the working tree"));
 +
 +              return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
 +      }
 +
 +      usage_with_options(git_submodule_helper_usage, module_config_options);
 +}
 +
  #define SUPPORT_SUPER_PREFIX (1<<0)
  
  struct cmd_struct {
@@@ -2192,9 -2030,7 +2206,9 @@@ static struct cmd_struct commands[] = 
        {"list", module_list, 0},
        {"name", module_name, 0},
        {"clone", module_clone, 0},
 +      {"update-module-mode", module_update_module_mode, 0},
        {"update-clone", update_clone, 0},
 +      {"ensure-core-worktree", ensure_core_worktree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
        {"check-name", check_name, 0},
 +      {"config", module_config, 0},
  };
  
  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --combined compat/mingw.c
index 34b3880b29d57eee6d6ae0afbb786d7980e7fa3e,886d60a46cc1aaff38b46c5ede3b4c0edc0a8923..a010e0b69d36f51787c07a55ef35096adb81bf6f
@@@ -5,8 -5,6 +5,8 @@@
  #include "../strbuf.h"
  #include "../run-command.h"
  #include "../cache.h"
 +#include "win32/lazyload.h"
 +#include "../config.h"
  
  #define HCAST(type, handle) ((type)(intptr_t)handle)
  
@@@ -204,60 -202,6 +204,60 @@@ static int ask_yes_no_if_possible(cons
        }
  }
  
 +/* Windows only */
 +enum hide_dotfiles_type {
 +      HIDE_DOTFILES_FALSE = 0,
 +      HIDE_DOTFILES_TRUE,
 +      HIDE_DOTFILES_DOTGITONLY
 +};
 +
 +static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 +static char *unset_environment_variables;
 +
 +int mingw_core_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcmp(var, "core.hidedotfiles")) {
 +              if (value && !strcasecmp(value, "dotgitonly"))
 +                      hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 +              else
 +                      hide_dotfiles = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "core.unsetenvvars")) {
 +              free(unset_environment_variables);
 +              unset_environment_variables = xstrdup(value);
 +              return 0;
 +      }
 +
 +      return 0;
 +}
 +
 +/* Normalizes NT paths as returned by some low-level APIs. */
 +static wchar_t *normalize_ntpath(wchar_t *wbuf)
 +{
 +      int i;
 +      /* fix absolute path prefixes */
 +      if (wbuf[0] == '\\') {
 +              /* strip NT namespace prefixes */
 +              if (!wcsncmp(wbuf, L"\\??\\", 4) ||
 +                  !wcsncmp(wbuf, L"\\\\?\\", 4))
 +                      wbuf += 4;
 +              else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
 +                      wbuf += 12;
 +              /* replace remaining '...UNC\' with '\\' */
 +              if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
 +                      wbuf += 2;
 +                      *wbuf = '\\';
 +              }
 +      }
 +      /* convert backslashes to slashes */
 +      for (i = 0; wbuf[i]; i++)
 +              if (wbuf[i] == '\\')
 +                      wbuf[i] = '/';
 +      return wbuf;
 +}
 +
  int mingw_unlink(const char *pathname)
  {
        int ret, tries = 0;
@@@ -389,6 -333,12 +389,12 @@@ int mingw_mkdir(const char *path, int m
  {
        int ret;
        wchar_t wpath[MAX_PATH];
+       if (!is_valid_win32_path(path)) {
+               errno = EINVAL;
+               return -1;
+       }
        if (xutftowcs_path(wpath, path) < 0)
                return -1;
        ret = _wmkdir(wpath);
@@@ -462,7 -412,7 +468,7 @@@ int mingw_open (const char *filename, i
        typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
        va_list args;
        unsigned mode;
-       int fd;
+       int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
        wchar_t wfilename[MAX_PATH];
        open_fn_t open_fn;
  
        mode = va_arg(args, int);
        va_end(args);
  
+       if (!is_valid_win32_path(filename)) {
+               errno = create ? EINVAL : ENOENT;
+               return -1;
+       }
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
  
@@@ -536,6 -491,11 +547,11 @@@ FILE *mingw_fopen (const char *filename
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
+       if (!is_valid_win32_path(filename)) {
+               int create = otype && strchr(otype, 'w');
+               errno = create ? EINVAL : ENOENT;
+               return NULL;
+       }
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        if (xutftowcs_path(wfilename, filename) < 0 ||
@@@ -558,6 -518,11 +574,11 @@@ FILE *mingw_freopen (const char *filena
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
+       if (!is_valid_win32_path(filename)) {
+               int create = otype && strchr(otype, 'w');
+               errno = create ? EINVAL : ENOENT;
+               return NULL;
+       }
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        if (xutftowcs_path(wfilename, filename) < 0 ||
@@@ -648,11 -613,9 +669,11 @@@ static inline long long filetime_to_hns
        return winTime - 116444736000000000LL;
  }
  
 -static inline time_t filetime_to_time_t(const FILETIME *ft)
 +static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
  {
 -      return (time_t)(filetime_to_hnsec(ft) / 10000000);
 +      long long hnsec = filetime_to_hnsec(ft);
 +      ts->tv_sec = (time_t)(hnsec / 10000000);
 +      ts->tv_nsec = (hnsec % 10000000) * 100;
  }
  
  /**
@@@ -711,9 -674,9 +732,9 @@@ static int do_lstat(int follow, const c
                buf->st_size = fdata.nFileSizeLow |
                        (((off_t)fdata.nFileSizeHigh)<<32);
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 -              buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 -              buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 -              buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
 +              filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 +              filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 +              filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                        WIN32_FIND_DATAW findbuf;
                        HANDLE handle = FindFirstFileW(wfilename, &findbuf);
@@@ -794,29 -757,6 +815,29 @@@ static int do_stat_internal(int follow
        return do_lstat(follow, alt_name, buf);
  }
  
 +static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
 +{
 +      BY_HANDLE_FILE_INFORMATION fdata;
 +
 +      if (!GetFileInformationByHandle(hnd, &fdata)) {
 +              errno = err_win_to_posix(GetLastError());
 +              return -1;
 +      }
 +
 +      buf->st_ino = 0;
 +      buf->st_gid = 0;
 +      buf->st_uid = 0;
 +      buf->st_nlink = 1;
 +      buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 +      buf->st_size = fdata.nFileSizeLow |
 +              (((off_t)fdata.nFileSizeHigh)<<32);
 +      buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 +      filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 +      filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 +      filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
 +      return 0;
 +}
 +
  int mingw_lstat(const char *file_name, struct stat *buf)
  {
        return do_stat_internal(0, file_name, buf);
@@@ -829,31 -769,32 +850,31 @@@ int mingw_stat(const char *file_name, s
  int mingw_fstat(int fd, struct stat *buf)
  {
        HANDLE fh = (HANDLE)_get_osfhandle(fd);
 -      BY_HANDLE_FILE_INFORMATION fdata;
 +      DWORD avail, type = GetFileType(fh) & ~FILE_TYPE_REMOTE;
  
 -      if (fh == INVALID_HANDLE_VALUE) {
 -              errno = EBADF;
 -              return -1;
 -      }
 -      /* direct non-file handles to MS's fstat() */
 -      if (GetFileType(fh) != FILE_TYPE_DISK)
 -              return _fstati64(fd, buf);
 +      switch (type) {
 +      case FILE_TYPE_DISK:
 +              return get_file_info_by_handle(fh, buf);
  
 -      if (GetFileInformationByHandle(fh, &fdata)) {
 -              buf->st_ino = 0;
 -              buf->st_gid = 0;
 -              buf->st_uid = 0;
 +      case FILE_TYPE_CHAR:
 +      case FILE_TYPE_PIPE:
 +              /* initialize stat fields */
 +              memset(buf, 0, sizeof(*buf));
                buf->st_nlink = 1;
 -              buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 -              buf->st_size = fdata.nFileSizeLow |
 -                      (((off_t)fdata.nFileSizeHigh)<<32);
 -              buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 -              buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 -              buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 -              buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
 +
 +              if (type == FILE_TYPE_CHAR) {
 +                      buf->st_mode = _S_IFCHR;
 +              } else {
 +                      buf->st_mode = _S_IFIFO;
 +                      if (PeekNamedPipe(fh, NULL, 0, NULL, &avail, NULL))
 +                              buf->st_size = avail;
 +              }
                return 0;
 +
 +      default:
 +              errno = EBADF;
 +              return -1;
        }
 -      errno = EBADF;
 -      return -1;
  }
  
  static inline void time_t_to_filetime(time_t t, FILETIME *ft)
@@@ -997,29 -938,8 +1018,29 @@@ struct tm *localtime_r(const time_t *ti
  
  char *mingw_getcwd(char *pointer, int len)
  {
 -      wchar_t wpointer[MAX_PATH];
 -      if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
 +      wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
 +      DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
 +
 +      if (!ret || ret >= ARRAY_SIZE(cwd)) {
 +              errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
 +              return NULL;
 +      }
 +      ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
 +      if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
 +              HANDLE hnd = CreateFileW(cwd, 0,
 +                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 +                      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 +              if (hnd == INVALID_HANDLE_VALUE)
 +                      return NULL;
 +              ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
 +              CloseHandle(hnd);
 +              if (!ret || ret >= ARRAY_SIZE(wpointer))
 +                      return NULL;
 +              if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
 +                      return NULL;
 +              return pointer;
 +      }
 +      if (!ret || ret >= ARRAY_SIZE(wpointer))
                return NULL;
        if (xwcstoutf(pointer, wpointer, len) < 0)
                return NULL;
  }
  
  /*
 - * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
 - * (Parsing C++ Command-Line Arguments)
 + * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
 + * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
   */
  static const char *quote_arg(const char *arg)
  {
                                p++;
                                len++;
                        }
-                       if (*p == '"')
+                       if (*p == '"' || !*p)
                                n += count*2 + 1;
                        continue;
                }
                                count++;
                                *d++ = *arg++;
                        }
-                       if (*arg == '"') {
+                       if (*arg == '"' || !*arg) {
                                while (count-- > 0)
                                        *d++ = '\\';
+                               /* don't escape the surrounding end quote */
+                               if (!*arg)
+                                       break;
                                *d++ = '\\';
                        }
                }
                *d++ = *arg++;
        }
        *d++ = '"';
-       *d++ = 0;
+       *d++ = '\0';
        return q;
  }
  
@@@ -1171,142 -1094,44 +1195,142 @@@ static char *path_lookup(const char *cm
        return prog;
  }
  
 -static int do_putenv(char **env, const char *name, int size, int free_old);
 +static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
 +{
 +      while (*s && *s != c)
 +              s++;
 +      return s;
 +}
 +
 +/* Compare only keys */
 +static int wenvcmp(const void *a, const void *b)
 +{
 +      wchar_t *p = *(wchar_t **)a, *q = *(wchar_t **)b;
 +      size_t p_len, q_len;
 +
 +      /* Find the keys */
 +      p_len = wcschrnul(p, L'=') - p;
 +      q_len = wcschrnul(q, L'=') - q;
 +
 +      /* If the length differs, include the shorter key's NUL */
 +      if (p_len < q_len)
 +              p_len++;
 +      else if (p_len > q_len)
 +              p_len = q_len + 1;
  
 -/* used number of elements of environ array, including terminating NULL */
 -static int environ_size = 0;
 -/* allocated size of environ array, in bytes */
 -static int environ_alloc = 0;
 +      return _wcsnicmp(p, q, p_len);
 +}
 +
 +/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
 +#ifndef INTERNAL_QSORT
 +#include "qsort.c"
 +#endif
  
  /*
 - * Create environment block suitable for CreateProcess. Merges current
 - * process environment and the supplied environment changes.
 + * Build an environment block combining the inherited environment
 + * merged with the given list of settings.
 + *
 + * Values of the form "KEY=VALUE" in deltaenv override inherited values.
 + * Values of the form "KEY" in deltaenv delete inherited values.
 + *
 + * Multiple entries in deltaenv for the same key are explicitly allowed.
 + *
 + * We return a contiguous block of UNICODE strings with a final trailing
 + * zero word.
   */
  static wchar_t *make_environment_block(char **deltaenv)
  {
 -      wchar_t *wenvblk = NULL;
 -      char **tmpenv;
 -      int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0;
 +      wchar_t *wenv = GetEnvironmentStringsW(), *wdeltaenv, *result, *p;
 +      size_t wlen, s, delta_size, size;
  
 -      while (deltaenv && deltaenv[i])
 -              i++;
 +      wchar_t **array = NULL;
 +      size_t alloc = 0, nr = 0, i;
  
 -      /* copy the environment, leaving space for changes */
 -      ALLOC_ARRAY(tmpenv, size + i);
 -      memcpy(tmpenv, environ, size * sizeof(char*));
 +      size = 1; /* for extra NUL at the end */
  
 -      /* merge supplied environment changes into the temporary environment */
 -      for (i = 0; deltaenv && deltaenv[i]; i++)
 -              size = do_putenv(tmpenv, deltaenv[i], size, 0);
 +      /* If there is no deltaenv to apply, simply return a copy. */
 +      if (!deltaenv || !*deltaenv) {
 +              for (p = wenv; p && *p; ) {
 +                      size_t s = wcslen(p) + 1;
 +                      size += s;
 +                      p += s;
 +              }
  
 -      /* create environment block from temporary environment */
 -      for (i = 0; tmpenv[i]; i++) {
 -              size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */
 -              ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz);
 -              wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1;
 +              ALLOC_ARRAY(result, size);
 +              memcpy(result, wenv, size * sizeof(*wenv));
 +              FreeEnvironmentStringsW(wenv);
 +              return result;
 +      }
 +
 +      /*
 +       * If there is a deltaenv, let's accumulate all keys into `array`,
 +       * sort them using the stable git_qsort() and then copy, skipping
 +       * duplicate keys
 +       */
 +      for (p = wenv; p && *p; ) {
 +              ALLOC_GROW(array, nr + 1, alloc);
 +              s = wcslen(p) + 1;
 +              array[nr++] = p;
 +              p += s;
 +              size += s;
 +      }
 +
 +      /* (over-)assess size needed for wchar version of deltaenv */
 +      for (delta_size = 0, i = 0; deltaenv[i]; i++)
 +              delta_size += strlen(deltaenv[i]) * 2 + 1;
 +      ALLOC_ARRAY(wdeltaenv, delta_size);
 +
 +      /* convert the deltaenv, appending to array */
 +      for (i = 0, p = wdeltaenv; deltaenv[i]; i++) {
 +              ALLOC_GROW(array, nr + 1, alloc);
 +              wlen = xutftowcs(p, deltaenv[i], wdeltaenv + delta_size - p);
 +              array[nr++] = p;
 +              p += wlen + 1;
 +      }
 +
 +      git_qsort(array, nr, sizeof(*array), wenvcmp);
 +      ALLOC_ARRAY(result, size + delta_size);
 +
 +      for (p = result, i = 0; i < nr; i++) {
 +              /* Skip any duplicate keys; last one wins */
 +              while (i + 1 < nr && !wenvcmp(array + i, array + i + 1))
 +                     i++;
 +
 +              /* Skip "to delete" entry */
 +              if (!wcschr(array[i], L'='))
 +                      continue;
 +
 +              size = wcslen(array[i]) + 1;
 +              memcpy(p, array[i], size * sizeof(*p));
 +              p += size;
 +      }
 +      *p = L'\0';
 +
 +      free(array);
 +      free(wdeltaenv);
 +      FreeEnvironmentStringsW(wenv);
 +      return result;
 +}
 +
 +static void do_unset_environment_variables(void)
 +{
 +      static int done;
 +      char *p = unset_environment_variables;
 +
 +      if (done || !p)
 +              return;
 +      done = 1;
 +
 +      for (;;) {
 +              char *comma = strchr(p, ',');
 +
 +              if (comma)
 +                      *comma = '\0';
 +              unsetenv(p);
 +              if (!comma)
 +                      break;
 +              p = comma + 1;
        }
 -      /* add final \0 terminator */
 -      wenvblk[wenvpos] = 0;
 -      free(tmpenv);
 -      return wenvblk;
  }
  
  struct pinfo_t {
@@@ -1327,12 -1152,9 +1351,12 @@@ static pid_t mingw_spawnve_fd(const cha
        wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
 +      HANDLE cons;
 +
 +      do_unset_environment_variables();
  
        /* Determine whether or not we are associated to a console */
 -      HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE,
 +      cons = CreateFile("CONOUT$", GENERIC_WRITE,
                        FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL, NULL);
        if (cons == INVALID_HANDLE_VALUE) {
@@@ -1551,83 -1373,87 +1575,83 @@@ int mingw_kill(pid_t pid, int sig
  }
  
  /*
 - * Compare environment entries by key (i.e. stopping at '=' or '\0').
 + * UTF-8 versions of getenv(), putenv() and unsetenv().
 + * Internally, they use the CRT's stock UNICODE routines
 + * to avoid data loss.
   */
 -static int compareenv(const void *v1, const void *v2)
 +char *mingw_getenv(const char *name)
  {
 -      const char *e1 = *(const char**)v1;
 -      const char *e2 = *(const char**)v2;
 +#define GETENV_MAX_RETAIN 30
 +      static char *values[GETENV_MAX_RETAIN];
 +      static int value_counter;
 +      int len_key, len_value;
 +      wchar_t *w_key;
 +      char *value;
 +      wchar_t w_value[32768];
  
 -      for (;;) {
 -              int c1 = *e1++;
 -              int c2 = *e2++;
 -              c1 = (c1 == '=') ? 0 : tolower(c1);
 -              c2 = (c2 == '=') ? 0 : tolower(c2);
 -              if (c1 > c2)
 -                      return 1;
 -              if (c1 < c2)
 -                      return -1;
 -              if (c1 == 0)
 -                      return 0;
 -      }
 -}
 +      if (!name || !*name)
 +              return NULL;
  
 -static int bsearchenv(char **env, const char *name, size_t size)
 -{
 -      unsigned low = 0, high = size;
 -      while (low < high) {
 -              unsigned mid = low + ((high - low) >> 1);
 -              int cmp = compareenv(&env[mid], &name);
 -              if (cmp < 0)
 -                      low = mid + 1;
 -              else if (cmp > 0)
 -                      high = mid;
 -              else
 -                      return mid;
 +      len_key = strlen(name) + 1;
 +      /* We cannot use xcalloc() here because that uses getenv() itself */
 +      w_key = calloc(len_key, sizeof(wchar_t));
 +      if (!w_key)
 +              die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
 +      xutftowcs(w_key, name, len_key);
 +      len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
 +      if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
 +              free(w_key);
 +              return NULL;
        }
 -      return ~low; /* not found, return 1's complement of insert position */
 +      free(w_key);
 +
 +      len_value = len_value * 3 + 1;
 +      /* We cannot use xcalloc() here because that uses getenv() itself */
 +      value = calloc(len_value, sizeof(char));
 +      if (!value)
 +              die("Out of memory, (tried to allocate %u bytes)", len_value);
 +      xwcstoutf(value, w_value, len_value);
 +
 +      /*
 +       * We return `value` which is an allocated value and the caller is NOT
 +       * expecting to have to free it, so we keep a round-robin array,
 +       * invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
 +       */
 +      free(values[value_counter]);
 +      values[value_counter++] = value;
 +      if (value_counter >= ARRAY_SIZE(values))
 +              value_counter = 0;
 +
 +      return value;
  }
  
 -/*
 - * If name contains '=', then sets the variable, otherwise it unsets it
 - * Size includes the terminating NULL. Env must have room for size + 1 entries
 - * (in case of insert). Returns the new size. Optionally frees removed entries.
 - */
 -static int do_putenv(char **env, const char *name, int size, int free_old)
 +int mingw_putenv(const char *namevalue)
  {
 -      int i = bsearchenv(env, name, size - 1);
 +      int size;
 +      wchar_t *wide, *equal;
 +      BOOL result;
  
 -      /* optionally free removed / replaced entry */
 -      if (i >= 0 && free_old)
 -              free(env[i]);
 +      if (!namevalue || !*namevalue)
 +              return 0;
  
 -      if (strchr(name, '=')) {
 -              /* if new value ('key=value') is specified, insert or replace entry */
 -              if (i < 0) {
 -                      i = ~i;
 -                      memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*));
 -                      size++;
 -              }
 -              env[i] = (char*) name;
 -      } else if (i >= 0) {
 -              /* otherwise ('key') remove existing entry */
 -              size--;
 -              memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*));
 +      size = strlen(namevalue) * 2 + 1;
 +      wide = calloc(size, sizeof(wchar_t));
 +      if (!wide)
 +              die("Out of memory, (tried to allocate %u wchar_t's)", size);
 +      xutftowcs(wide, namevalue, size);
 +      equal = wcschr(wide, L'=');
 +      if (!equal)
 +              result = SetEnvironmentVariableW(wide, NULL);
 +      else {
 +              *equal = L'\0';
 +              result = SetEnvironmentVariableW(wide, equal + 1);
        }
 -      return size;
 -}
 +      free(wide);
  
 -char *mingw_getenv(const char *name)
 -{
 -      char *value;
 -      int pos = bsearchenv(environ, name, environ_size - 1);
 -      if (pos < 0)
 -              return NULL;
 -      value = strchr(environ[pos], '=');
 -      return value ? &value[1] : NULL;
 -}
 +      if (!result)
 +              errno = err_win_to_posix(GetLastError());
  
 -int mingw_putenv(const char *namevalue)
 -{
 -      ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
 -      environ_size = do_putenv(environ, namevalue, environ_size, 1);
 -      return 0;
 +      return result ? 0 : -1;
  }
  
  /*
@@@ -1775,8 -1601,7 +1799,8 @@@ static void ensure_socket_initializatio
                        WSAGetLastError());
  
        for (name = libraries; *name; name++) {
 -              ipv6_dll = LoadLibrary(*name);
 +              ipv6_dll = LoadLibraryExA(*name, NULL,
 +                                        LOAD_LIBRARY_SEARCH_SYSTEM32);
                if (!ipv6_dll)
                        continue;
  
@@@ -1997,63 -1822,18 +2021,63 @@@ int mingw_getpagesize(void
        return si.dwAllocationGranularity;
  }
  
 +/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
 +enum EXTENDED_NAME_FORMAT {
 +      NameDisplay = 3,
 +      NameUserPrincipal = 8
 +};
 +
 +static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
 +{
 +      DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
 +              enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
 +      static wchar_t wbuffer[1024];
 +      DWORD len;
 +
 +      if (!INIT_PROC_ADDR(GetUserNameExW))
 +              return NULL;
 +
 +      len = ARRAY_SIZE(wbuffer);
 +      if (GetUserNameExW(type, wbuffer, &len)) {
 +              char *converted = xmalloc((len *= 3));
 +              if (xwcstoutf(converted, wbuffer, len) >= 0)
 +                      return converted;
 +              free(converted);
 +      }
 +
 +      return NULL;
 +}
 +
 +char *mingw_query_user_email(void)
 +{
 +      return get_extended_user_info(NameUserPrincipal);
 +}
 +
  struct passwd *getpwuid(int uid)
  {
 +      static unsigned initialized;
        static char user_name[100];
 -      static struct passwd p;
 +      static struct passwd *p;
 +      DWORD len;
  
 -      DWORD len = sizeof(user_name);
 -      if (!GetUserName(user_name, &len))
 +      if (initialized)
 +              return p;
 +
 +      len = sizeof(user_name);
 +      if (!GetUserName(user_name, &len)) {
 +              initialized = 1;
                return NULL;
 -      p.pw_name = user_name;
 -      p.pw_gecos = "unknown";
 -      p.pw_dir = NULL;
 -      return &p;
 +      }
 +
 +      p = xmalloc(sizeof(*p));
 +      p->pw_name = user_name;
 +      p->pw_gecos = get_extended_user_info(NameDisplay);
 +      if (!p->pw_gecos)
 +              p->pw_gecos = "unknown";
 +      p->pw_dir = NULL;
 +
 +      initialized = 1;
 +      return p;
  }
  
  static HANDLE timer_event;
@@@ -2211,12 -1991,24 +2235,12 @@@ int mingw_raise(int sig
  
  int link(const char *oldpath, const char *newpath)
  {
 -      typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
 -      static T create_hard_link = NULL;
        wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
        if (xutftowcs_path(woldpath, oldpath) < 0 ||
                xutftowcs_path(wnewpath, newpath) < 0)
                return -1;
  
 -      if (!create_hard_link) {
 -              create_hard_link = (T) GetProcAddress(
 -                      GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
 -              if (!create_hard_link)
 -                      create_hard_link = (T)-1;
 -      }
 -      if (create_hard_link == (T)-1) {
 -              errno = ENOSYS;
 -              return -1;
 -      }
 -      if (!create_hard_link(wnewpath, woldpath, NULL)) {
 +      if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
                errno = err_win_to_posix(GetLastError());
                return -1;
        }
@@@ -2275,6 -2067,30 +2299,30 @@@ pid_t waitpid(pid_t pid, int *status, i
        return -1;
  }
  
+ int mingw_has_dos_drive_prefix(const char *path)
+ {
+       int i;
+       /*
+        * Does it start with an ASCII letter (i.e. highest bit not set),
+        * followed by a colon?
+        */
+       if (!(0x80 & (unsigned char)*path))
+               return *path && path[1] == ':' ? 2 : 0;
+       /*
+        * While drive letters must be letters of the English alphabet, it is
+        * possible to assign virtually _any_ Unicode character via `subst` as
+        * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
+        * like this:
+        *
+        *      subst ֍: %USERPROFILE%\Desktop
+        */
+       for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
+               ; /* skip first UTF-8 character */
+       return path[i] == ':' ? i + 1 : 0;
+ }
  int mingw_skip_dos_drive_prefix(char **path)
  {
        int ret = has_dos_drive_prefix(*path);
@@@ -2416,6 -2232,50 +2464,50 @@@ static void setup_windows_environment(v
                setenv("TERM", "cygwin", 1);
  }
  
+ int is_valid_win32_path(const char *path)
+ {
+       int preceding_space_or_period = 0, i = 0, periods = 0;
+       if (!protect_ntfs)
+               return 1;
+       skip_dos_drive_prefix((char **)&path);
+       for (;;) {
+               char c = *(path++);
+               switch (c) {
+               case '\0':
+               case '/': case '\\':
+                       /* cannot end in ` ` or `.`, except for `.` and `..` */
+                       if (preceding_space_or_period &&
+                           (i != periods || periods > 2))
+                               return 0;
+                       if (!c)
+                               return 1;
+                       i = periods = preceding_space_or_period = 0;
+                       continue;
+               case '.':
+                       periods++;
+                       /* fallthru */
+               case ' ':
+                       preceding_space_or_period = 1;
+                       i++;
+                       continue;
+               case ':': /* DOS drive prefix was already skipped */
+               case '<': case '>': case '"': case '|': case '?': case '*':
+                       /* illegal character */
+                       return 0;
+               default:
+                       if (c > '\0' && c < '\x20')
+                               /* illegal character */
+                               return 0;
+               }
+               preceding_space_or_period = 0;
+               i++;
+       }
+ }
  /*
   * Disable MSVCRT command line wildcard expansion (__getmainargs called from
   * mingw startup code, see init.c in mingw runtime).
@@@ -2523,6 -2383,17 +2615,6 @@@ void mingw_startup(void
        maxlen = wcslen(wargv[0]);
        for (i = 1; i < argc; i++)
                maxlen = max(maxlen, wcslen(wargv[i]));
 -      for (i = 0; wenv[i]; i++)
 -              maxlen = max(maxlen, wcslen(wenv[i]));
 -
 -      /*
 -       * nedmalloc can't free CRT memory, allocate resizable environment
 -       * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot
 -       * use it while initializing the environment itself.
 -       */
 -      environ_size = i + 1;
 -      environ_alloc = alloc_nr(environ_size * sizeof(char*));
 -      environ = malloc_startup(environ_alloc);
  
        /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
        maxlen = 3 * maxlen + 1;
        /* convert command line arguments and environment to UTF-8 */
        for (i = 0; i < argc; i++)
                __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
 -      for (i = 0; wenv[i]; i++)
 -              environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen);
 -      environ[i] = NULL;
        free(buffer);
  
 -      /* sort environment for O(log n) getenv / putenv */
 -      qsort(environ, i, sizeof(char*), compareenv);
 -
        /* fix Windows specific environment settings */
        setup_windows_environment();
  
 +      unset_environment_variables = xstrdup("PERL5LIB");
 +
        /* initialize critical section for waitpid pinfo_t list */
        InitializeCriticalSection(&pinfo_cs);
  
diff --combined compat/mingw.h
index 8c24ddaa3efc20e4454ebc87c51fa30316f64a22,5b43e6557e17a92688d1c51143ee205dc6be176a..163ae1b59e2a1d444daa0087174f9a347b47e6b0
@@@ -11,9 -11,6 +11,9 @@@ typedef _sigset_t sigset_t
  #undef _POSIX_THREAD_SAFE_FUNCTIONS
  #endif
  
 +extern int mingw_core_config(const char *var, const char *value, void *cb);
 +#define platform_core_config mingw_core_config
 +
  /*
   * things that are not available in header files
   */
@@@ -260,35 -257,11 +260,35 @@@ char *mingw_mktemp(char *template)
  char *mingw_getcwd(char *pointer, int len);
  #define getcwd mingw_getcwd
  
 +#ifdef NO_UNSETENV
 +#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
 +#endif
 +
 +/*
 + * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
 + * These talk to the CRT using UNICODE/wchar_t, but maintain the original
 + * narrow-char API.
 + *
 + * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
 + * routines and stores both versions of each environment variable in parallel
 + * (and secretly updates both when you set one or the other), but it uses CP_ACP
 + * to do the conversion rather than CP_UTF8.
 + *
 + * Since everything in the git code base is UTF8, we define the mingw_ routines
 + * to access the CRT using the UNICODE routines and manually convert them to
 + * UTF8.  This also avoids round-trip problems.
 + *
 + * This also helps with our linkage, since "_wenviron" is publicly exported
 + * from the CRT.  But to access "_environ" we would have to statically link
 + * to the CRT (/MT).
 + *
 + * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
 + */
 +#define getenv       mingw_getenv
 +#define putenv       mingw_putenv
 +#define unsetenv     mingw_putenv
  char *mingw_getenv(const char *name);
 -#define getenv mingw_getenv
 -int mingw_putenv(const char *namevalue);
 -#define putenv mingw_putenv
 -#define unsetenv mingw_putenv
 +int   mingw_putenv(const char *name);
  
  int mingw_gethostname(char *host, int namelen);
  #define gethostname mingw_gethostname
@@@ -354,41 -327,18 +354,41 @@@ static inline int getrlimit(int resourc
  }
  
  /*
 - * Use mingw specific stat()/lstat()/fstat() implementations on Windows.
 + * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
 + * including our own struct stat with 64 bit st_size and nanosecond-precision
 + * file times.
   */
  #ifndef __MINGW64_VERSION_MAJOR
  #define off_t off64_t
  #define lseek _lseeki64
 +struct timespec {
 +      time_t tv_sec;
 +      long tv_nsec;
 +};
  #endif
  
 -/* use struct stat with 64 bit st_size */
 +struct mingw_stat {
 +    _dev_t st_dev;
 +    _ino_t st_ino;
 +    _mode_t st_mode;
 +    short st_nlink;
 +    short st_uid;
 +    short st_gid;
 +    _dev_t st_rdev;
 +    off64_t st_size;
 +    struct timespec st_atim;
 +    struct timespec st_mtim;
 +    struct timespec st_ctim;
 +};
 +
 +#define st_atime st_atim.tv_sec
 +#define st_mtime st_mtim.tv_sec
 +#define st_ctime st_ctim.tv_sec
 +
  #ifdef stat
  #undef stat
  #endif
 -#define stat _stati64
 +#define stat mingw_stat
  int mingw_lstat(const char *file_name, struct stat *buf);
  int mingw_stat(const char *file_name, struct stat *buf);
  int mingw_fstat(int fd, struct stat *buf);
  #endif
  #define lstat mingw_lstat
  
 -#ifndef _stati64
 -# define _stati64(x,y) mingw_stat(x,y)
 -#elif defined (_USE_32BIT_TIME_T)
 -# define _stat32i64(x,y) mingw_stat(x,y)
 -#else
 -# define _stat64(x,y) mingw_stat(x,y)
 -#endif
  
  int mingw_utime(const char *file_name, const struct utimbuf *times);
  #define utime mingw_utime
@@@ -433,9 -390,6 +433,9 @@@ int mingw_raise(int sig)
  int winansi_isatty(int fd);
  #define isatty winansi_isatty
  
 +int winansi_dup2(int oldfd, int newfd);
 +#define dup2 winansi_dup2
 +
  void winansi_init(void);
  HANDLE winansi_get_osfhandle(int fd);
  
   * git specific compatibility
   */
  
- #define has_dos_drive_prefix(path) \
-       (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+ int mingw_has_dos_drive_prefix(const char *path);
+ #define has_dos_drive_prefix mingw_has_dos_drive_prefix
  int mingw_skip_dos_drive_prefix(char **path);
  #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
  static inline int mingw_is_dir_sep(int c)
@@@ -470,8 -424,6 +470,8 @@@ static inline void convert_slashes(cha
  int mingw_offset_1st_component(const char *path);
  #define offset_1st_component mingw_offset_1st_component
  #define PATH_SEP ';'
 +extern char *mingw_query_user_email(void);
 +#define query_user_email mingw_query_user_email
  #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
  #define PRIuMAX "I64u"
  #define PRId64 "I64d"
  #include <inttypes.h>
  #endif
  
+ /**
+  * Verifies that the given path is a valid one on Windows.
+  *
+  * In particular, path segments are disallowed which
+  *
+  * - end in a period or a space (except the special directories `.` and `..`).
+  *
+  * - contain any of the reserved characters, e.g. `:`, `;`, `*`, etc
+  *
+  * Returns 1 upon success, otherwise 0.
+  */
+ int is_valid_win32_path(const char *path);
+ #define is_valid_path(path) is_valid_win32_path(path)
  /**
   * Converts UTF-8 encoded string to UTF-16LE.
   *
diff --combined config.mak.uname
index 3ee7da0e230c4c33e79ae2f3cf498a5cd7cc4881,d0bd271ff6058c37ec53ce0a54e974e6821dd306..85af9f9cf1081335b786ddab7907cebb37ceb66d
@@@ -370,6 -370,7 +370,6 @@@ ifeq ($(uname_S),Windows
        RUNTIME_PREFIX = YesPlease
        HAVE_WPGMPTR = YesWeDo
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 -      NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        # USE_NED_ALLOCATOR = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
        ETAGS_TARGET = ETAGS
 -      NO_INET_PTON = YesPlease
 -      NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        NATIVE_CRLF = YesPlease
        DEFAULT_HELP_FORMAT = html
        EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
        PTHREAD_LIBS =
        lib =
-       BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
  ifndef DEBUG
        BASIC_CFLAGS += -GL -Os -MD
        BASIC_LDFLAGS += -LTCG
@@@ -430,6 -432,8 +429,6 @@@ ifeq ($(uname_S),Minix
        NO_NSEC = YesPlease
        NEEDS_LIBGEN =
        NEEDS_CRYPTO_WITH_SSL = YesPlease
 -      NEEDS_IDN_WITH_CURL = YesPlease
 -      NEEDS_SSL_WITH_CURL = YesPlease
        NEEDS_RESOLV =
        NO_HSTRERROR = YesPlease
        NO_MMAP = YesPlease
@@@ -455,6 -459,7 +454,6 @@@ ifeq ($(uname_S),NONSTOP_KERNEL
        # Missdetected, hence commented out, see below.
        #NO_CURL = YesPlease
        # Added manually, see above.
 -      NEEDS_SSL_WITH_CURL = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_LIBICONV = YesPlease
@@@ -514,6 -519,7 +513,6 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        RUNTIME_PREFIX = YesPlease
        HAVE_WPGMPTR = YesWeDo
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 -      NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        USE_NED_ALLOCATOR = YesPlease
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        ETAGS_TARGET = ETAGS
 -      NO_INET_PTON = YesPlease
 -      NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        DEFAULT_HELP_FORMAT = html
        COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
-       BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
+       BASIC_CFLAGS += -DWIN32
        EXTLIBS += -lws2_32
        GITLIBS += git.res
        PTHREAD_LIBS =
diff --combined connect.c
index 24281b608284ee74b262237c467ff054874d8a8e,93778aa25b0d664d7aadd02368983f305debe0d0..79f1b3b24257a100cfd338eebdb465e0069e0953
+++ b/connect.c
@@@ -224,7 -224,7 +224,7 @@@ static int process_dummy_ref(const cha
                return 0;
        name++;
  
 -      return !oidcmp(&null_oid, &oid) && !strcmp(name, "capabilities^{}");
 +      return oideq(&null_oid, &oid) && !strcmp(name, "capabilities^{}");
  }
  
  static void check_no_capabilities(const char *line, int len)
@@@ -514,7 -514,7 +514,7 @@@ int url_is_local_not_ssh(const char *ur
        const char *colon = strchr(url, ':');
        const char *slash = strchr(url, '/');
        return !colon || (slash && slash < colon) ||
-               has_dos_drive_prefix(url);
+               (has_dos_drive_prefix(url) && is_valid_path(url));
  }
  
  static const char *prot_name(enum protocol protocol)
diff --combined environment.c
index 346559770773e923766c06593ada84c72815061f,8c34e99f4e5b8c409bf5ffad69b482c6be00368b..c7b76d3bbb1143f870685ac13ea70a0d333c6121
@@@ -33,7 -33,6 +33,7 @@@ int ref_paranoia = -1
  int repository_format_precious_objects;
  char *repository_format_partial_clone;
  const char *core_partial_clone_filter_default;
 +int repository_format_worktree_config;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
  const char *apply_default_whitespace;
@@@ -72,6 -71,7 +72,6 @@@ int core_apply_sparse_checkout
  int merge_log_config = -1;
  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
  unsigned long pack_size_limit_cfg;
 -enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
  enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
  
  #ifndef PROTECT_HFS_DEFAULT
@@@ -80,7 -80,7 +80,7 @@@
  int protect_hfs = PROTECT_HFS_DEFAULT;
  
  #ifndef PROTECT_NTFS_DEFAULT
- #define PROTECT_NTFS_DEFAULT 0
+ #define PROTECT_NTFS_DEFAULT 1
  #endif
  int protect_ntfs = PROTECT_NTFS_DEFAULT;
  const char *core_fsmonitor;
diff --combined fast-import.c
index 69886687ce95fd7dfe5363d0c776e21241b6b830,7321ccca57adc6350f209aca0db160b412e04bfd..c8b372bc4abbfdd6b3b3e31cc12bfe2ac0d9e838
@@@ -171,7 -171,6 +171,7 @@@ Format of STDIN stream
  #include "packfile.h"
  #include "object-store.h"
  #include "mem-pool.h"
 +#include "commit-reach.h"
  
  #define PACK_ID_BITS 16
  #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@@ -364,6 -363,7 +364,7 @@@ static uintmax_t next_mark
  static struct strbuf new_data = STRBUF_INIT;
  static int seen_data_command;
  static int require_explicit_termination;
+ static int allow_unsafe_features;
  
  /* Signal handling */
  static volatile sig_atomic_t checkpoint_requested;
@@@ -573,7 -573,7 +574,7 @@@ static struct object_entry *find_object
        unsigned int h = oid->hash[0] << 8 | oid->hash[1];
        struct object_entry *e;
        for (e = object_table[h]; e; e = e->next)
 -              if (!oidcmp(oid, &e->idx.oid))
 +              if (oideq(oid, &e->idx.oid))
                        return e;
        return NULL;
  }
@@@ -584,7 -584,7 +585,7 @@@ static struct object_entry *insert_obje
        struct object_entry *e = object_table[h];
  
        while (e) {
 -              if (!oidcmp(oid, &e->idx.oid))
 +              if (oideq(oid, &e->idx.oid))
                        return e;
                e = e->next;
        }
@@@ -1069,7 -1069,7 +1070,7 @@@ static int store_object
                duplicate_count_by_type[type]++;
                return 1;
        } else if (find_sha1_pack(oid.hash,
 -                                get_packed_git(the_repository))) {
 +                                get_all_packs(the_repository))) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@@ -1267,7 -1267,7 +1268,7 @@@ static void stream_blob(uintmax_t len, 
                truncate_pack(&checkpoint);
  
        } else if (find_sha1_pack(oid.hash,
 -                                get_packed_git(the_repository))) {
 +                                get_all_packs(the_repository))) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
                e->idx.offset = 1; /* just not zero! */
@@@ -1534,7 -1534,7 +1535,7 @@@ static int tree_content_set
                        if (!*slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
 -                                              && !oidcmp(&e->versions[1].oid, oid))
 +                                              && oideq(&e->versions[1].oid, oid))
                                        return 0;
                                e->versions[1].mode = mode;
                                oidcpy(&e->versions[1].oid, oid);
@@@ -1826,6 -1826,12 +1827,12 @@@ static void dump_marks(void
        if (!export_marks_file || (import_marks_file && !import_marks_file_done))
                return;
  
+       if (safe_create_leading_directories_const(export_marks_file)) {
+               failure |= error_errno("unable to create leading directories of %s",
+                                      export_marks_file);
+               return;
+       }
        if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
                failure |= error_errno("Unable to write marks file %s",
                                       export_marks_file);
@@@ -2650,7 -2656,7 +2657,7 @@@ static int parse_from(struct branch *b
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
 -              if (oidcmp(&b->oid, &oe->idx.oid)) {
 +              if (!oideq(&b->oid, &oe->idx.oid)) {
                        oidcpy(&b->oid, &oe->idx.oid);
                        if (oe->pack_id != MAX_PACK_ID) {
                                unsigned long size;
        else
                die("Invalid ref name or SHA1 expression: %s", from);
  
 -      if (b->branch_tree.tree && oidcmp(&oid, &b->branch_tree.versions[1].oid)) {
 +      if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
                release_tree_content_recursive(b->branch_tree.tree);
                b->branch_tree.tree = NULL;
        }
@@@ -2955,8 -2961,8 +2962,8 @@@ static void cat_blob(struct object_entr
                die("Object %s is a %s but a blob was expected.",
                    oid_to_hex(oid), type_name(type));
        strbuf_reset(&line);
 -      strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid),
 -                                              type_name(type), size);
 +      strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
 +                  type_name(type), (uintmax_t)size);
        cat_blob_write(line.buf, line.len);
        strbuf_release(&line);
        cat_blob_write(buf, size);
@@@ -3198,7 -3204,6 +3205,6 @@@ static void option_import_marks(const c
        }
  
        import_marks_file = make_fast_import_path(marks);
-       safe_create_leading_directories_const(import_marks_file);
        import_marks_file_from_stream = from_stream;
        import_marks_file_ignore_missing = ignore_missing;
  }
@@@ -3239,7 -3244,6 +3245,6 @@@ static void option_active_branches(cons
  static void option_export_marks(const char *marks)
  {
        export_marks_file = make_fast_import_path(marks);
-       safe_create_leading_directories_const(export_marks_file);
  }
  
  static void option_cat_blob_fd(const char *fd)
@@@ -3282,10 -3286,12 +3287,12 @@@ static int parse_one_option(const char 
                option_active_branches(option);
        } else if (skip_prefix(option, "export-pack-edges=", &option)) {
                option_export_pack_edges(option);
-       } else if (starts_with(option, "quiet")) {
+       } else if (!strcmp(option, "quiet")) {
                show_stats = 0;
-       } else if (starts_with(option, "stats")) {
+       } else if (!strcmp(option, "stats")) {
                show_stats = 1;
+       } else if (!strcmp(option, "allow-unsafe-features")) {
+               ; /* already handled during early option parsing */
        } else {
                return 0;
        }
        return 1;
  }
  
+ static void check_unsafe_feature(const char *feature, int from_stream)
+ {
+       if (from_stream && !allow_unsafe_features)
+               die(_("feature '%s' forbidden in input without --allow-unsafe-features"),
+                   feature);
+ }
  static int parse_one_feature(const char *feature, int from_stream)
  {
        const char *arg;
        if (skip_prefix(feature, "date-format=", &arg)) {
                option_date_format(arg);
        } else if (skip_prefix(feature, "import-marks=", &arg)) {
+               check_unsafe_feature("import-marks", from_stream);
                option_import_marks(arg, from_stream, 0);
        } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
+               check_unsafe_feature("import-marks-if-exists", from_stream);
                option_import_marks(arg, from_stream, 1);
        } else if (skip_prefix(feature, "export-marks=", &arg)) {
+               check_unsafe_feature(feature, from_stream);
                option_export_marks(arg);
        } else if (!strcmp(feature, "get-mark")) {
                ; /* Don't die - this feature is supported */
@@@ -3430,6 -3446,20 +3447,20 @@@ int cmd_main(int argc, const char **arg
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
        marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
  
+       /*
+        * We don't parse most options until after we've seen the set of
+        * "feature" lines at the start of the stream (which allows the command
+        * line to override stream data). But we must do an early parse of any
+        * command-line options that impact how we interpret the feature lines.
+        */
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (*arg != '-' || !strcmp(arg, "--"))
+                       break;
+               if (!strcmp(arg, "--allow-unsafe-features"))
+                       allow_unsafe_features = 1;
+       }
        global_argc = argc;
        global_argv = argv;
  
diff --combined fsck.c
index 68502ce85b11bf0ef4445f2b1688b3834301ba53,78ce79cfee058739784df25d7836a2f321ea95ea..535f806c67e72875e91021c8ce3dab6b8d783c26
--- 1/fsck.c
--- 2/fsck.c
+++ b/fsck.c
@@@ -10,6 -10,7 +10,6 @@@
  #include "fsck.h"
  #include "refs.h"
  #include "utf8.h"
 -#include "sha1-array.h"
  #include "decorate.h"
  #include "oidset.h"
  #include "packfile.h"
@@@ -68,6 -69,7 +68,7 @@@ static struct oidset gitmodules_done = 
        FUNC(GITMODULES_SYMLINK, ERROR) \
        FUNC(GITMODULES_URL, ERROR) \
        FUNC(GITMODULES_PATH, ERROR) \
+       FUNC(GITMODULES_UPDATE, ERROR) \
        /* warnings */ \
        FUNC(BAD_FILEMODE, WARN) \
        FUNC(EMPTY_NAME, WARN) \
@@@ -183,37 -185,40 +184,37 @@@ static int fsck_msg_type(enum fsck_msg_
  
  static void init_skiplist(struct fsck_options *options, const char *path)
  {
 -      static struct oid_array skiplist = OID_ARRAY_INIT;
 -      int sorted, fd;
 -      char buffer[GIT_MAX_HEXSZ + 1];
 +      FILE *fp;
 +      struct strbuf sb = STRBUF_INIT;
        struct object_id oid;
  
 -      if (options->skiplist)
 -              sorted = options->skiplist->sorted;
 -      else {
 -              sorted = 1;
 -              options->skiplist = &skiplist;
 -      }
 -
 -      fd = open(path, O_RDONLY);
 -      if (fd < 0)
 +      fp = fopen(path, "r");
 +      if (!fp)
                die("Could not open skip list: %s", path);
 -      for (;;) {
 +      while (!strbuf_getline(&sb, fp)) {
                const char *p;
 -              int result = read_in_full(fd, buffer, sizeof(buffer));
 -              if (result < 0)
 -                      die_errno("Could not read '%s'", path);
 -              if (!result)
 -                      break;
 -              if (parse_oid_hex(buffer, &oid, &p) || *p != '\n')
 -                      die("Invalid SHA-1: %s", buffer);
 -              oid_array_append(&skiplist, &oid);
 -              if (sorted && skiplist.nr > 1 &&
 -                              oidcmp(&skiplist.oid[skiplist.nr - 2],
 -                                     &oid) > 0)
 -                      sorted = 0;
 -      }
 -      close(fd);
 +              const char *hash;
 +
 +              /*
 +               * Allow trailing comments, leading whitespace
 +               * (including before commits), and empty or whitespace
 +               * only lines.
 +               */
 +              hash = strchr(sb.buf, '#');
 +              if (hash)
 +                      strbuf_setlen(&sb, hash - sb.buf);
 +              strbuf_trim(&sb);
 +              if (!sb.len)
 +                      continue;
  
 -      if (sorted)
 -              skiplist.sorted = 1;
 +              if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
 +                      die("Invalid SHA-1: %s", sb.buf);
 +              oidset_insert(&options->skiplist, &oid);
 +      }
 +      if (ferror(fp))
 +              die_errno("Could not read '%s'", path);
 +      fclose(fp);
 +      strbuf_release(&sb);
  }
  
  static int parse_msg_type(const char *str)
@@@ -318,7 -323,9 +319,7 @@@ static void append_msg_id(struct strbu
  
  static int object_on_skiplist(struct fsck_options *opts, struct object *obj)
  {
 -      if (opts && opts->skiplist && obj)
 -              return oid_array_lookup(opts->skiplist, &obj->oid) >= 0;
 -      return 0;
 +      return opts && obj && oidset_contains(&opts->skiplist, &obj->oid);
  }
  
  __attribute__((format (printf, 4, 5)))
@@@ -605,7 -612,7 +606,7 @@@ static int fsck_tree(struct tree *item
  
        while (desc.size) {
                unsigned mode;
-               const char *name;
+               const char *name, *backslash;
                const struct object_id *oid;
  
                oid = tree_entry_extract(&desc, &name, &mode);
                                                 ".gitmodules is a symbolic link");
                }
  
+               if ((backslash = strchr(name, '\\'))) {
+                       while (backslash) {
+                               backslash++;
+                               has_dotgit |= is_ntfs_dotgit(backslash);
+                               if (is_ntfs_dotgitmodules(backslash)) {
+                                       if (!S_ISLNK(mode))
+                                               oidset_insert(&gitmodules_found, oid);
+                                       else
+                                               retval += report(options, &item->object,
+                                                                FSCK_MSG_GITMODULES_SYMLINK,
+                                                                ".gitmodules is a symbolic link");
+                               }
+                               backslash = strchr(backslash, '\\');
+                       }
+               }
                if (update_tree_entry_gently(&desc)) {
                        retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
                        break;
@@@ -1000,6 -1023,12 +1017,12 @@@ static int fsck_gitmodules_fn(const cha
                                    FSCK_MSG_GITMODULES_PATH,
                                    "disallowed submodule path: %s",
                                    value);
+       if (!strcmp(key, "update") && value &&
+           parse_submodule_update_type(value) == SM_UPDATE_COMMAND)
+               data->ret |= report(data->options, data->obj,
+                                   FSCK_MSG_GITMODULES_UPDATE,
+                                   "disallowed submodule update setting: %s",
+                                   value);
        free(name);
  
        return 0;
diff --combined git-compat-util.h
index 09b0102cae8c8c0e39dc239003ca599a896730cf,16316c62e550c5c6607bc7b9c07b423e4ad71c89..d17360e89789403ece1d81278822e7469cd296a1
  #define _SGI_SOURCE 1
  
  #if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
 -# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
 -#  define _WIN32_WINNT 0x0502
 +# if !defined(_WIN32_WINNT)
 +#  define _WIN32_WINNT 0x0600
  # endif
  #define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
  #include <winsock2.h>
  #include <regex.h>
  #include <utime.h>
  #include <syslog.h>
 -#ifndef NO_SYS_POLL_H
 +#if !defined(NO_POLL_H)
 +#include <poll.h>
 +#elif !defined(NO_SYS_POLL_H)
  #include <sys/poll.h>
  #else
 +/* Pull the compat stuff */
  #include <poll.h>
  #endif
  #ifdef HAVE_BSD_SYSCTL
@@@ -345,14 -342,6 +345,14 @@@ typedef uintmax_t timestamp_t
  #define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
  #endif
  
 +#ifndef platform_core_config
 +static inline int noop_core_config(const char *var, const char *value, void *cb)
 +{
 +      return 0;
 +}
 +#define platform_core_config noop_core_config
 +#endif
 +
  #ifndef has_dos_drive_prefix
  static inline int git_has_dos_drive_prefix(const char *path)
  {
@@@ -385,6 -374,10 +385,10 @@@ static inline int git_offset_1st_compon
  #define offset_1st_component git_offset_1st_component
  #endif
  
+ #ifndef is_valid_path
+ #define is_valid_path(path) 1
+ #endif
  #ifndef find_last_dir_sep
  static inline char *git_find_last_dir_sep(const char *path)
  {
  #define find_last_dir_sep git_find_last_dir_sep
  #endif
  
 +#ifndef query_user_email
 +#define query_user_email() NULL
 +#endif
 +
  #if defined(__HP_cc) && (__HP_cc >= 61000)
  #define NORETURN __attribute__((noreturn))
  #define NORETURN_PTR
  #define LAST_ARG_MUST_BE_NULL
  #endif
  
 +#define MAYBE_UNUSED __attribute__((__unused__))
 +
  #include "compat/bswap.h"
  
  #include "wildmatch.h"
@@@ -861,7 -848,6 +865,7 @@@ extern FILE *fopen_or_warn(const char *
  #define FREE_AND_NULL(p) do { free(p); (p) = NULL; } while (0)
  
  #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
 +#define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x)));
  #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
  
  #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
diff --combined git-submodule.sh
index 5e608f8bad305fea40e9063b854bf1be24ebd448,4bc2f9d75224595ff0ecc74c9878ccb32005fd02..65d62c888c8f2288ca276b28744f51f773bee8f2
@@@ -34,6 -34,7 +34,7 @@@ reference
  cached=
  recursive=
  init=
+ require_init=
  files=
  remote=
  nofetch=
@@@ -72,7 -73,7 +73,7 @@@ get_submodule_config () 
        value=$(git config submodule."$name"."$option")
        if test -z "$value"
        then
 -              value=$(git config -f .gitmodules submodule."$name"."$option")
 +              value=$(git submodule--helper config submodule."$name"."$option")
        fi
        printf '%s' "${value:-$default}"
  }
@@@ -82,11 -83,6 +83,11 @@@ isnumber(
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
  }
  
 +# Given a full hex object ID, is this the zero OID?
 +is_zero_oid () {
 +      echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1
 +}
 +
  # Sanitize the local git environment for use within a submodule. We
  # can't simply use clear_local_git_env since we want to preserve some
  # of the settings from GIT_CONFIG_PARAMETERS.
@@@ -164,11 -160,6 +165,11 @@@ cmd_add(
                shift
        done
  
 +      if ! git submodule--helper config --check-writeable >/dev/null 2>&1
 +      then
 +               die "$(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
 +      fi
 +
        if test -n "$reference_path"
        then
                is_absolute_path "$reference_path" ||
@@@ -293,11 -284,11 +294,11 @@@ or you are unsure what this means choos
        git add --no-warn-embedded-repo $force "$sm_path" ||
        die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
  
 -      git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
 -      git config -f .gitmodules submodule."$sm_name".url "$repo" &&
 +      git submodule--helper config submodule."$sm_name".path "$sm_path" &&
 +      git submodule--helper config submodule."$sm_name".url "$repo" &&
        if test -n "$branch"
        then
 -              git config -f .gitmodules submodule."$sm_name".branch "$branch"
 +              git submodule--helper config submodule."$sm_name".branch "$branch"
        fi &&
        git add --force .gitmodules ||
        die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
@@@ -457,6 -448,10 +458,10 @@@ cmd_update(
                -i|--init)
                        init=1
                        ;;
+               --require-init)
+                       init=1
+                       require_init=1
+                       ;;
                --remote)
                        remote=1
                        ;;
                ${reference:+"$reference"} \
                ${dissociate:+"--dissociate"} \
                ${depth:+--depth "$depth"} \
+               ${require_init:+--require-init} \
                $recommend_shallow \
                $jobs \
                "$@" || echo "#unmatched" $?
        } | {
        err=
 -      while read -r mode sha1 stage just_cloned sm_path
 +      while read -r quickabort sha1 just_cloned sm_path
        do
 -              die_if_unmatched "$mode" "$sha1"
 +              die_if_unmatched "$quickabort" "$sha1"
  
 -              name=$(git submodule--helper name "$sm_path") || exit
 -              if ! test -z "$update"
 -              then
 -                      update_module=$update
 -              else
 -                      update_module=$(git config submodule."$name".update)
 -                      if test -z "$update_module"
 -                      then
 -                              update_module="checkout"
 -                      fi
 -              fi
 +              git submodule--helper ensure-core-worktree "$sm_path"
 +
 +              update_module=$(git submodule--helper update-module-mode $just_cloned "$sm_path" $update)
  
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
  
                if test $just_cloned -eq 1
                then
                        subsha1=
 -                      case "$update_module" in
 -                      merge | rebase | none)
 -                              update_module=checkout ;;
 -                      esac
                else
                        subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
                                must_die_on_failure=yes
                                ;;
                        *)
 -                              die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
 +                              die "$(eval_gettext "Invalid update mode '$update_module' for submodule path '$path'")"
                        esac
  
                        if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")
@@@ -790,7 -798,7 +796,7 @@@ cmd_summary() 
        while read -r mod_src mod_dst sha1_src sha1_dst status name
        do
                if test -z "$cached" &&
 -                      test $sha1_dst = 0000000000000000000000000000000000000000
 +                      is_zero_oid $sha1_dst
                then
                        case "$mod_dst" in
                        160000)
diff --combined path.c
index dc3294c71e1e72ae2aae9164c790e27d1237e956,736d2e335d98c0282b81abe6e7ac1088b095b1f1..617545cd5d4390e4d171485b2ca13f010330b21e
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -108,7 -108,6 +108,7 @@@ struct common_dir 
  
  static struct common_dir common_list[] = {
        { 0, 1, 0, "branches" },
 +      { 0, 1, 0, "common" },
        { 0, 1, 0, "hooks" },
        { 0, 1, 0, "info" },
        { 0, 0, 1, "info/sparse-checkout" },
        { 0, 1, 0, "objects" },
        { 0, 1, 0, "refs" },
        { 0, 1, 1, "refs/bisect" },
 +      { 0, 1, 1, "refs/worktree" },
        { 0, 1, 0, "remotes" },
        { 0, 1, 0, "worktrees" },
        { 0, 1, 0, "rr-cache" },
@@@ -1292,37 -1290,77 +1292,77 @@@ int daemon_avoid_alias(const char *p
        }
  }
  
- static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+ /*
+  * On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
+  * directory:
+  *
+  * - For historical reasons, file names that end in spaces or periods are
+  *   automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
+  *   to `.git/`.
+  *
+  * - For other historical reasons, file names that do not conform to the 8.3
+  *   format (up to eight characters for the basename, three for the file
+  *   extension, certain characters not allowed such as `+`, etc) are associated
+  *   with a so-called "short name", at least on the `C:` drive by default.
+  *   Which means that `git~1/` is a valid way to refer to `.git/`.
+  *
+  *   Note: Technically, `.git/` could receive the short name `git~2` if the
+  *   short name `git~1` were already used. In Git, however, we guarantee that
+  *   `.git` is the first item in a directory, therefore it will be associated
+  *   with the short name `git~1` (unless short names are disabled).
+  *
+  * - For yet other historical reasons, NTFS supports so-called "Alternate Data
+  *   Streams", i.e. metadata associated with a given file, referred to via
+  *   `<filename>:<stream-name>:<stream-type>`. There exists a default stream
+  *   type for directories, allowing `.git/` to be accessed via
+  *   `.git::$INDEX_ALLOCATION/`.
+  *
+  * When this function returns 1, it indicates that the specified file/directory
+  * name refers to a `.git` file or directory, or to any of these synonyms, and
+  * Git should therefore not track it.
+  *
+  * For performance reasons, _all_ Alternate Data Streams of `.git/` are
+  * forbidden, not just `::$INDEX_ALLOCATION`.
+  *
+  * This function is intended to be used by `git fsck` even on platforms where
+  * the backslash is a regular filename character, therefore it needs to handle
+  * backlash characters in the provided `name` specially: they are interpreted
+  * as directory separators.
+  */
+ int is_ntfs_dotgit(const char *name)
  {
-       if (len < skip)
+       char c;
+       /*
+        * Note that when we don't find `.git` or `git~1` we end up with `name`
+        * advanced partway through the string. That's okay, though, as we
+        * return immediately in those cases, without looking at `name` any
+        * further.
+        */
+       c = *(name++);
+       if (c == '.') {
+               /* .git */
+               if (((c = *(name++)) != 'g' && c != 'G') ||
+                   ((c = *(name++)) != 'i' && c != 'I') ||
+                   ((c = *(name++)) != 't' && c != 'T'))
+                       return 0;
+       } else if (c == 'g' || c == 'G') {
+               /* git ~1 */
+               if (((c = *(name++)) != 'i' && c != 'I') ||
+                   ((c = *(name++)) != 't' && c != 'T') ||
+                   *(name++) != '~' ||
+                   *(name++) != '1')
+                       return 0;
+       } else
                return 0;
-       len -= skip;
-       path += skip;
-       while (len-- > 0) {
-               char c = *(path++);
-               if (c != ' ' && c != '.')
+       for (;;) {
+               c = *(name++);
+               if (!c || c == '\\' || c == '/' || c == ':')
+                       return 1;
+               if (c != '.' && c != ' ')
                        return 0;
        }
-       return 1;
- }
- int is_ntfs_dotgit(const char *name)
- {
-       size_t len;
-       for (len = 0; ; len++)
-               if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
-                       if (only_spaces_and_periods(name, len, 4) &&
-                                       !strncasecmp(name, ".git", 4))
-                               return 1;
-                       if (only_spaces_and_periods(name, len, 5) &&
-                                       !strncasecmp(name, "git~1", 5))
-                               return 1;
-                       if (name[len] != '\\')
-                               return 0;
-                       name += len + 1;
-                       len = -1;
-               }
  }
  
  static int is_ntfs_dot_generic(const char *name,
  only_spaces_and_periods:
                for (;;) {
                        char c = name[i++];
-                       if (!c)
+                       if (!c || c == ':')
                                return 1;
                        if (c != ' ' && c != '.')
                                return 0;
diff --combined read-cache.c
index bd45dc3e24d7dc28820d26cc6e6d377f92fda46d,a07bd95eb8df771bbb4ecc26fa41ac0e8eb606dd..1d82dbdd65c8f01db24d2e40b702988a412f1034
@@@ -23,8 -23,6 +23,8 @@@
  #include "split-index.h"
  #include "utf8.h"
  #include "fsmonitor.h"
 +#include "thread-utils.h"
 +#include "progress.h"
  
  /* Mask for the name length in ce_flags in the on-disk index */
  
@@@ -45,8 -43,6 +45,8 @@@
  #define CACHE_EXT_LINK 0x6c696e6b       /* "link" */
  #define CACHE_EXT_UNTRACKED 0x554E5452          /* "UNTR" */
  #define CACHE_EXT_FSMONITOR 0x46534D4E          /* "FSMN" */
 +#define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945        /* "EOIE" */
 +#define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */
  
  /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
  #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@@ -209,17 -205,15 +209,17 @@@ void fill_stat_cache_info(struct cache_
        }
  }
  
 -static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 +static int ce_compare_data(struct index_state *istate,
 +                         const struct cache_entry *ce,
 +                         struct stat *st)
  {
        int match = -1;
        int fd = git_open_cloexec(ce->name, O_RDONLY);
  
        if (fd >= 0) {
                struct object_id oid;
 -              if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
 -                      match = oidcmp(&oid, &ce->oid);
 +              if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
 +                      match = !oideq(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@@ -260,16 -254,14 +260,16 @@@ static int ce_compare_gitlink(const str
         */
        if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
 -      return oidcmp(&oid, &ce->oid);
 +      return !oideq(&oid, &ce->oid);
  }
  
 -static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
 +static int ce_modified_check_fs(struct index_state *istate,
 +                              const struct cache_entry *ce,
 +                              struct stat *st)
  {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
 -              if (ce_compare_data(ce, st))
 +              if (ce_compare_data(istate, ce, st))
                        return DATA_CHANGED;
                break;
        case S_IFLNK:
@@@ -415,7 -407,7 +415,7 @@@ int ie_match_stat(struct index_state *i
                if (assume_racy_is_modified)
                        changed |= DATA_CHANGED;
                else
 -                      changed |= ce_modified_check_fs(ce, st);
 +                      changed |= ce_modified_check_fs(istate, ce, st);
        }
  
        return changed;
@@@ -455,7 -447,7 +455,7 @@@ int ie_modified(struct index_state *ist
            (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
  
 -      changed_fs = ce_modified_check_fs(ce, st);
 +      changed_fs = ce_modified_check_fs(istate, ce, st);
        if (changed_fs)
                return changed | changed_fs;
        return 0;
@@@ -761,7 -753,7 +761,7 @@@ int add_to_index(struct index_state *is
                }
        }
        if (!intent_only) {
 -              if (index_path(&ce->oid, path, st, newflags)) {
 +              if (index_path(istate, &ce->oid, path, st, newflags)) {
                        discard_cache_entry(ce);
                        return error("unable to index file %s", path);
                }
        /* It was suspected to be racily clean, but it turns out to be Ok */
        was_same = (alias &&
                    !ce_stage(alias) &&
 -                  !oidcmp(&alias->oid, &ce->oid) &&
 +                  oideq(&alias->oid, &ce->oid) &&
                    ce->ce_mode == alias->ce_mode);
  
        if (pretend)
@@@ -831,7 -823,7 +831,7 @@@ struct cache_entry *make_cache_entry(st
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
  
 -      ret = refresh_cache_entry(&the_index, ce, refresh_options);
 +      ret = refresh_cache_entry(istate, ce, refresh_options);
        if (ret != ce)
                discard_cache_entry(ce);
        return ret;
@@@ -955,6 -947,9 +955,9 @@@ int verify_path(const char *path, unsig
        if (has_dos_drive_prefix(path))
                return 0;
  
+       if (!is_valid_path(path))
+               return 0;
        goto inside;
        for (;;) {
                if (!c)
@@@ -982,7 -977,15 +985,15 @@@ inside
                        if ((c == '.' && !verify_dotfile(path, mode)) ||
                            is_dir_sep(c) || c == '\0')
                                return 0;
+               } else if (c == '\\' && protect_ntfs) {
+                       if (is_ntfs_dotgit(path))
+                               return 0;
+                       if (S_ISLNK(mode)) {
+                               if (is_ntfs_dotgitmodules(path))
+                                       return 0;
+                       }
                }
                c = *path++;
        }
  }
@@@ -1484,24 -1487,13 +1495,24 @@@ int refresh_index(struct index_state *i
        const char *typechange_fmt;
        const char *added_fmt;
        const char *unmerged_fmt;
 -      uint64_t start = getnanotime();
 +      struct progress *progress = NULL;
 +
 +      if (flags & REFRESH_PROGRESS && isatty(2))
 +              progress = start_delayed_progress(_("Refresh index"),
 +                                                istate->cache_nr);
  
 +      trace_performance_enter();
        modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
        deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
        typechange_fmt = (in_porcelain ? "T\t%s\n" : "%s needs update\n");
        added_fmt = (in_porcelain ? "A\t%s\n" : "%s needs update\n");
        unmerged_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
 +      /*
 +       * Use the multi-threaded preload_index() to refresh most of the
 +       * cache entries quickly then in the single threaded loop below,
 +       * we only have to do the special cases that are left.
 +       */
 +      preload_index(istate, pathspec, 0);
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce, *new_entry;
                int cache_errno = 0;
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
  
 -              if (pathspec && !ce_path_match(&the_index, ce, pathspec, seen))
 +              if (pathspec && !ce_path_match(istate, ce, pathspec, seen))
                        filtered = 1;
  
                if (ce_stage(ce)) {
                new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
                if (new_entry == ce)
                        continue;
 +              if (progress)
 +                      display_progress(progress, i);
                if (!new_entry) {
                        const char *fmt;
  
  
                replace_index_entry(istate, i, new_entry);
        }
 -      trace_performance_since(start, "refresh index");
 +      if (progress) {
 +              display_progress(progress, istate->cache_nr);
 +              stop_progress(&progress);
 +      }
 +      trace_performance_leave("refresh index");
        return has_errors;
  }
  
@@@ -1675,7 -1661,7 +1686,7 @@@ int verify_index_checksum
  /* Allow fsck to force verification of the cache entry order. */
  int verify_ce_order;
  
 -static int verify_hdr(struct cache_header *hdr, unsigned long size)
 +static int verify_hdr(const struct cache_header *hdr, unsigned long size)
  {
        git_hash_ctx c;
        unsigned char hash[GIT_MAX_RAWSZ];
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
        the_hash_algo->final_fn(hash, &c);
 -      if (hashcmp(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
 +      if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
                return error("bad index file sha1 signature");
        return 0;
  }
  
  static int read_index_extension(struct index_state *istate,
 -                              const char *ext, void *data, unsigned long sz)
 +                              const char *ext, const char *data, unsigned long sz)
  {
        switch (CACHE_EXT(ext)) {
        case CACHE_EXT_TREE:
        case CACHE_EXT_FSMONITOR:
                read_fsmonitor_extension(istate, data, sz);
                break;
 +      case CACHE_EXT_ENDOFINDEXENTRIES:
 +      case CACHE_EXT_INDEXENTRYOFFSETTABLE:
 +              /* already handled in do_read_index() */
 +              break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@@ -1742,25 -1724,63 +1753,25 @@@ int read_index(struct index_state *ista
        return read_index_from(istate, get_index_file(), get_git_dir());
  }
  
 -static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool,
 -                                                 struct ondisk_cache_entry *ondisk,
 -                                                 unsigned int flags,
 -                                                 const char *name,
 -                                                 size_t len)
 -{
 -      struct cache_entry *ce = mem_pool__ce_alloc(mem_pool, len);
 -
 -      ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
 -      ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
 -      ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
 -      ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
 -      ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
 -      ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
 -      ce->ce_mode  = get_be32(&ondisk->mode);
 -      ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
 -      ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
 -      ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
 -      ce->ce_flags = flags & ~CE_NAMEMASK;
 -      ce->ce_namelen = len;
 -      ce->index = 0;
 -      hashcpy(ce->oid.hash, ondisk->sha1);
 -      memcpy(ce->name, name, len);
 -      ce->name[len] = '\0';
 -      return ce;
 -}
 -
 -/*
 - * Adjacent cache entries tend to share the leading paths, so it makes
 - * sense to only store the differences in later entries.  In the v4
 - * on-disk format of the index, each on-disk cache entry stores the
 - * number of bytes to be stripped from the end of the previous name,
 - * and the bytes to append to the result, to come up with its name.
 - */
 -static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
 -{
 -      const unsigned char *ep, *cp = (const unsigned char *)cp_;
 -      size_t len = decode_varint(&cp);
 -
 -      if (name->len < len)
 -              die("malformed name field in the index");
 -      strbuf_remove(name, name->len - len, len);
 -      for (ep = cp; *ep; ep++)
 -              ; /* find the end */
 -      strbuf_add(name, cp, ep - cp);
 -      return (const char *)ep + 1 - cp_;
 -}
 -
 -static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
 +static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
 +                                          unsigned int version,
                                            struct ondisk_cache_entry *ondisk,
                                            unsigned long *ent_size,
 -                                          struct strbuf *previous_name)
 +                                          const struct cache_entry *previous_ce)
  {
        struct cache_entry *ce;
        size_t len;
        const char *name;
        unsigned int flags;
 +      size_t copy_len = 0;
 +      /*
 +       * Adjacent cache entries tend to share the leading paths, so it makes
 +       * sense to only store the differences in later entries.  In the v4
 +       * on-disk format of the index, each on-disk cache entry stores the
 +       * number of bytes to be stripped from the end of the previous name,
 +       * and the bytes to append to the result, to come up with its name.
 +       */
 +      int expand_name_field = version == 4;
  
        /* On-disk flags are just 16 bits */
        flags = get_be16(&ondisk->flags);
        else
                name = ondisk->name;
  
 -      if (!previous_name) {
 -              /* v3 and earlier */
 -              if (len == CE_NAMEMASK)
 -                      len = strlen(name);
 -              ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len);
 +      if (expand_name_field) {
 +              const unsigned char *cp = (const unsigned char *)name;
 +              size_t strip_len, previous_len;
 +
 +              /* If we're at the begining of a block, ignore the previous name */
 +              strip_len = decode_varint(&cp);
 +              if (previous_ce) {
 +                      previous_len = previous_ce->ce_namelen;
 +                      if (previous_len < strip_len)
 +                              die(_("malformed name field in the index, near path '%s'"),
 +                                      previous_ce->name);
 +                      copy_len = previous_len - strip_len;
 +              }
 +              name = (const char *)cp;
 +      }
  
 -              *ent_size = ondisk_ce_size(ce);
 -      } else {
 -              unsigned long consumed;
 -              consumed = expand_name_field(previous_name, name);
 -              ce = cache_entry_from_ondisk(mem_pool, ondisk, flags,
 -                                           previous_name->buf,
 -                                           previous_name->len);
 +      if (len == CE_NAMEMASK) {
 +              len = strlen(name);
 +              if (expand_name_field)
 +                      len += copy_len;
 +      }
 +
 +      ce = mem_pool__ce_alloc(ce_mem_pool, len);
 +
 +      ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
 +      ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
 +      ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
 +      ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
 +      ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
 +      ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
 +      ce->ce_mode  = get_be32(&ondisk->mode);
 +      ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
 +      ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
 +      ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
 +      ce->ce_flags = flags & ~CE_NAMEMASK;
 +      ce->ce_namelen = len;
 +      ce->index = 0;
 +      hashcpy(ce->oid.hash, ondisk->sha1);
  
 -              *ent_size = (name - ((char *)ondisk)) + consumed;
 +      if (expand_name_field) {
 +              if (copy_len)
 +                      memcpy(ce->name, previous_ce->name, copy_len);
 +              memcpy(ce->name + copy_len, name, len + 1 - copy_len);
 +              *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;
 +      } else {
 +              memcpy(ce->name, name, len + 1);
 +              *ent_size = ondisk_ce_size(ce);
        }
        return ce;
  }
@@@ -1912,228 -1900,16 +1923,228 @@@ static size_t estimate_cache_size(size_
        return ondisk_size + entries * per_entry;
  }
  
 +struct index_entry_offset
 +{
 +      /* starting byte offset into index file, count of index entries in this block */
 +      int offset, nr;
 +};
 +
 +struct index_entry_offset_table
 +{
 +      int nr;
 +      struct index_entry_offset entries[FLEX_ARRAY];
 +};
 +
 +static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset);
 +static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);
 +
 +static size_t read_eoie_extension(const char *mmap, size_t mmap_size);
 +static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);
 +
 +struct load_index_extensions
 +{
 +      pthread_t pthread;
 +      struct index_state *istate;
 +      const char *mmap;
 +      size_t mmap_size;
 +      unsigned long src_offset;
 +};
 +
 +static void *load_index_extensions(void *_data)
 +{
 +      struct load_index_extensions *p = _data;
 +      unsigned long src_offset = p->src_offset;
 +
 +      while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) {
 +              /* After an array of active_nr index entries,
 +               * there can be arbitrary number of extended
 +               * sections, each of which is prefixed with
 +               * extension name (4-byte) and section length
 +               * in 4-byte network byte order.
 +               */
 +              uint32_t extsize = get_be32(p->mmap + src_offset + 4);
 +              if (read_index_extension(p->istate,
 +                                       p->mmap + src_offset,
 +                                       p->mmap + src_offset + 8,
 +                                       extsize) < 0) {
 +                      munmap((void *)p->mmap, p->mmap_size);
 +                      die(_("index file corrupt"));
 +              }
 +              src_offset += 8;
 +              src_offset += extsize;
 +      }
 +
 +      return NULL;
 +}
 +
 +/*
 + * A helper function that will load the specified range of cache entries
 + * from the memory mapped file and add them to the given index.
 + */
 +static unsigned long load_cache_entry_block(struct index_state *istate,
 +                      struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
 +                      unsigned long start_offset, const struct cache_entry *previous_ce)
 +{
 +      int i;
 +      unsigned long src_offset = start_offset;
 +
 +      for (i = offset; i < offset + nr; i++) {
 +              struct ondisk_cache_entry *disk_ce;
 +              struct cache_entry *ce;
 +              unsigned long consumed;
 +
 +              disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
 +              ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
 +              set_index_entry(istate, i, ce);
 +
 +              src_offset += consumed;
 +              previous_ce = ce;
 +      }
 +      return src_offset - start_offset;
 +}
 +
 +static unsigned long load_all_cache_entries(struct index_state *istate,
 +                      const char *mmap, size_t mmap_size, unsigned long src_offset)
 +{
 +      unsigned long consumed;
 +
 +      if (istate->version == 4) {
 +              mem_pool_init(&istate->ce_mem_pool,
 +                              estimate_cache_size_from_compressed(istate->cache_nr));
 +      } else {
 +              mem_pool_init(&istate->ce_mem_pool,
 +                              estimate_cache_size(mmap_size, istate->cache_nr));
 +      }
 +
 +      consumed = load_cache_entry_block(istate, istate->ce_mem_pool,
 +                                      0, istate->cache_nr, mmap, src_offset, NULL);
 +      return consumed;
 +}
 +
 +/*
 + * Mostly randomly chosen maximum thread counts: we
 + * cap the parallelism to online_cpus() threads, and we want
 + * to have at least 10000 cache entries per thread for it to
 + * be worth starting a thread.
 + */
 +
 +#define THREAD_COST           (10000)
 +
 +struct load_cache_entries_thread_data
 +{
 +      pthread_t pthread;
 +      struct index_state *istate;
 +      struct mem_pool *ce_mem_pool;
 +      int offset;
 +      const char *mmap;
 +      struct index_entry_offset_table *ieot;
 +      int ieot_start;         /* starting index into the ieot array */
 +      int ieot_blocks;        /* count of ieot entries to process */
 +      unsigned long consumed; /* return # of bytes in index file processed */
 +};
 +
 +/*
 + * A thread proc to run the load_cache_entries() computation
 + * across multiple background threads.
 + */
 +static void *load_cache_entries_thread(void *_data)
 +{
 +      struct load_cache_entries_thread_data *p = _data;
 +      int i;
 +
 +      /* iterate across all ieot blocks assigned to this thread */
 +      for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) {
 +              p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool,
 +                      p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL);
 +              p->offset += p->ieot->entries[i].nr;
 +      }
 +      return NULL;
 +}
 +
 +static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
 +                      unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot)
 +{
 +      int i, offset, ieot_blocks, ieot_start, err;
 +      struct load_cache_entries_thread_data *data;
 +      unsigned long consumed = 0;
 +
 +      /* a little sanity checking */
 +      if (istate->name_hash_initialized)
 +              BUG("the name hash isn't thread safe");
 +
 +      mem_pool_init(&istate->ce_mem_pool, 0);
 +
 +      /* ensure we have no more threads than we have blocks to process */
 +      if (nr_threads > ieot->nr)
 +              nr_threads = ieot->nr;
 +      data = xcalloc(nr_threads, sizeof(*data));
 +
 +      offset = ieot_start = 0;
 +      ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads);
 +      for (i = 0; i < nr_threads; i++) {
 +              struct load_cache_entries_thread_data *p = &data[i];
 +              int nr, j;
 +
 +              if (ieot_start + ieot_blocks > ieot->nr)
 +                      ieot_blocks = ieot->nr - ieot_start;
 +
 +              p->istate = istate;
 +              p->offset = offset;
 +              p->mmap = mmap;
 +              p->ieot = ieot;
 +              p->ieot_start = ieot_start;
 +              p->ieot_blocks = ieot_blocks;
 +
 +              /* create a mem_pool for each thread */
 +              nr = 0;
 +              for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
 +                      nr += p->ieot->entries[j].nr;
 +              if (istate->version == 4) {
 +                      mem_pool_init(&p->ce_mem_pool,
 +                              estimate_cache_size_from_compressed(nr));
 +              } else {
 +                      mem_pool_init(&p->ce_mem_pool,
 +                              estimate_cache_size(mmap_size, nr));
 +              }
 +
 +              err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p);
 +              if (err)
 +                      die(_("unable to create load_cache_entries thread: %s"), strerror(err));
 +
 +              /* increment by the number of cache entries in the ieot block being processed */
 +              for (j = 0; j < ieot_blocks; j++)
 +                      offset += ieot->entries[ieot_start + j].nr;
 +              ieot_start += ieot_blocks;
 +      }
 +
 +      for (i = 0; i < nr_threads; i++) {
 +              struct load_cache_entries_thread_data *p = &data[i];
 +
 +              err = pthread_join(p->pthread, NULL);
 +              if (err)
 +                      die(_("unable to join load_cache_entries thread: %s"), strerror(err));
 +              mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool);
 +              consumed += p->consumed;
 +      }
 +
 +      free(data);
 +
 +      return consumed;
 +}
 +
  /* remember to discard_cache() before reading a different cache! */
  int do_read_index(struct index_state *istate, const char *path, int must_exist)
  {
 -      int fd, i;
 +      int fd;
        struct stat st;
        unsigned long src_offset;
 -      struct cache_header *hdr;
 -      void *mmap;
 +      const struct cache_header *hdr;
 +      const char *mmap;
        size_t mmap_size;
 -      struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 +      struct load_index_extensions p;
 +      size_t extension_offset = 0;
 +      int nr_threads, cpus;
 +      struct index_entry_offset_table *ieot = NULL;
  
        if (istate->initialized)
                return istate->cache_nr;
                die_errno("unable to map index file");
        close(fd);
  
 -      hdr = mmap;
 +      hdr = (const struct cache_header *)mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
  
        istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
        istate->initialized = 1;
  
 -      if (istate->version == 4) {
 -              previous_name = &previous_name_buf;
 -              mem_pool_init(&istate->ce_mem_pool,
 -                            estimate_cache_size_from_compressed(istate->cache_nr));
 -      } else {
 -              previous_name = NULL;
 -              mem_pool_init(&istate->ce_mem_pool,
 -                            estimate_cache_size(mmap_size, istate->cache_nr));
 -      }
 +      p.istate = istate;
 +      p.mmap = mmap;
 +      p.mmap_size = mmap_size;
  
        src_offset = sizeof(*hdr);
 -      for (i = 0; i < istate->cache_nr; i++) {
 -              struct ondisk_cache_entry *disk_ce;
 -              struct cache_entry *ce;
 -              unsigned long consumed;
  
 -              disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
 -              ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name);
 -              set_index_entry(istate, i, ce);
 +      if (git_config_get_index_threads(&nr_threads))
 +              nr_threads = 1;
  
 -              src_offset += consumed;
 +      /* TODO: does creating more threads than cores help? */
 +      if (!nr_threads) {
 +              nr_threads = istate->cache_nr / THREAD_COST;
 +              cpus = online_cpus();
 +              if (nr_threads > cpus)
 +                      nr_threads = cpus;
        }
 -      strbuf_release(&previous_name_buf);
 +
 +      if (!HAVE_THREADS)
 +              nr_threads = 1;
 +
 +      if (nr_threads > 1) {
 +              extension_offset = read_eoie_extension(mmap, mmap_size);
 +              if (extension_offset) {
 +                      int err;
 +
 +                      p.src_offset = extension_offset;
 +                      err = pthread_create(&p.pthread, NULL, load_index_extensions, &p);
 +                      if (err)
 +                              die(_("unable to create load_index_extensions thread: %s"), strerror(err));
 +
 +                      nr_threads--;
 +              }
 +      }
 +
 +      /*
 +       * Locate and read the index entry offset table so that we can use it
 +       * to multi-thread the reading of the cache entries.
 +       */
 +      if (extension_offset && nr_threads > 1)
 +              ieot = read_ieot_extension(mmap, mmap_size, extension_offset);
 +
 +      if (ieot) {
 +              src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot);
 +              free(ieot);
 +      } else {
 +              src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
 +      }
 +
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
  
 -      while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) {
 -              /* After an array of active_nr index entries,
 -               * there can be arbitrary number of extended
 -               * sections, each of which is prefixed with
 -               * extension name (4-byte) and section length
 -               * in 4-byte network byte order.
 -               */
 -              uint32_t extsize;
 -              memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
 -              extsize = ntohl(extsize);
 -              if (read_index_extension(istate,
 -                                       (const char *) mmap + src_offset,
 -                                       (char *) mmap + src_offset + 8,
 -                                       extsize) < 0)
 -                      goto unmap;
 -              src_offset += 8;
 -              src_offset += extsize;
 +      /* if we created a thread, join it otherwise load the extensions on the primary thread */
 +      if (extension_offset) {
 +              int ret = pthread_join(p.pthread, NULL);
 +              if (ret)
 +                      die(_("unable to join load_index_extensions thread: %s"), strerror(ret));
 +      } else {
 +              p.src_offset = src_offset;
 +              load_index_extensions(&p);
        }
 -      munmap(mmap, mmap_size);
 +      munmap((void *)mmap, mmap_size);
        return istate->cache_nr;
  
  unmap:
 -      munmap(mmap, mmap_size);
 +      munmap((void *)mmap, mmap_size);
        die("index file corrupt");
  }
  
@@@ -2253,6 -2013,7 +2264,6 @@@ static void freshen_shared_index(const 
  int read_index_from(struct index_state *istate, const char *path,
                    const char *gitdir)
  {
 -      uint64_t start = getnanotime();
        struct split_index *split_index;
        int ret;
        char *base_oid_hex;
        if (istate->initialized)
                return istate->cache_nr;
  
 +      trace_performance_enter();
        ret = do_read_index(istate, path, 0);
 -      trace_performance_since(start, "read cache %s", path);
 +      trace_performance_leave("read cache %s", path);
  
        split_index = istate->split_index;
        if (!split_index || is_null_oid(&split_index->base_oid)) {
                return ret;
        }
  
 +      trace_performance_enter();
        if (split_index->base)
                discard_index(split_index->base);
        else
        base_oid_hex = oid_to_hex(&split_index->base_oid);
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
        ret = do_read_index(split_index->base, base_path, 1);
 -      if (oidcmp(&split_index->base_oid, &split_index->base->oid))
 +      if (!oideq(&split_index->base_oid, &split_index->base->oid))
                die("broken index, expect %s in %s, got %s",
                    base_oid_hex, base_path,
                    oid_to_hex(&split_index->base->oid));
        freshen_shared_index(base_path, 0);
        merge_base_index(istate);
        post_read_index_from(istate);
 -      trace_performance_since(start, "read cache %s", base_path);
 +      trace_performance_leave("read cache %s", base_path);
        free(base_path);
        return ret;
  }
@@@ -2374,7 -2133,7 +2385,7 @@@ int unmerged_index(const struct index_s
        return 0;
  }
  
 -int index_has_changes(const struct index_state *istate,
 +int index_has_changes(struct index_state *istate,
                      struct tree *tree,
                      struct strbuf *sb)
  {
        if (tree || !get_oid_tree("HEAD", &cmp)) {
                struct diff_options opt;
  
 -              diff_setup(&opt);
 +              repo_diff_setup(the_repository, &opt);
                opt.flags.exit_with_status = 1;
                if (!sb)
                        opt.flags.quick = 1;
@@@ -2450,15 -2209,11 +2461,15 @@@ static int ce_write(git_hash_ctx *conte
        return 0;
  }
  
 -static int write_index_ext_header(git_hash_ctx *context, int fd,
 -                                unsigned int ext, unsigned int sz)
 +static int write_index_ext_header(git_hash_ctx *context, git_hash_ctx *eoie_context,
 +                                int fd, unsigned int ext, unsigned int sz)
  {
        ext = htonl(ext);
        sz = htonl(sz);
 +      if (eoie_context) {
 +              the_hash_algo->update_fn(eoie_context, &ext, 4);
 +              the_hash_algo->update_fn(eoie_context, &sz, 4);
 +      }
        return ((ce_write(context, fd, &ext, 4) < 0) ||
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
  }
@@@ -2486,8 -2241,7 +2497,8 @@@ static int ce_flush(git_hash_ctx *conte
        return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
  }
  
 -static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
 +static void ce_smudge_racily_clean_entry(struct index_state *istate,
 +                                       struct cache_entry *ce)
  {
        /*
         * The only thing we care about in this function is to smudge the
                return;
        if (ce_match_stat_basic(ce, &st))
                return;
 -      if (ce_modified_check_fs(ce, &st)) {
 +      if (ce_modified_check_fs(istate, ce, &st)) {
                /* This is "racily clean"; smudge it.  Note that this
                 * is a tricky code.  At first glance, it may appear
                 * that it can break with this sequence:
@@@ -2652,7 -2406,7 +2663,7 @@@ static int verify_index_from(const stru
        if (n != the_hash_algo->rawsz)
                goto out;
  
 -      if (hashcmp(istate->oid.hash, hash))
 +      if (!hasheq(istate->oid.hash, hash))
                goto out;
  
        close(fd);
@@@ -2690,36 -2444,6 +2701,36 @@@ void update_index_if_able(struct index_
                rollback_lock_file(lockfile);
  }
  
 +static int record_eoie(void)
 +{
 +      int val;
 +
 +      if (!git_config_get_bool("index.recordendofindexentries", &val))
 +              return val;
 +
 +      /*
 +       * As a convenience, the end of index entries extension
 +       * used for threading is written by default if the user
 +       * explicitly requested threaded index reads.
 +       */
 +      return !git_config_get_index_threads(&val) && val != 1;
 +}
 +
 +static int record_ieot(void)
 +{
 +      int val;
 +
 +      if (!git_config_get_bool("index.recordoffsettable", &val))
 +              return val;
 +
 +      /*
 +       * As a convenience, the offset table used for threading is
 +       * written by default if the user explicitly requested
 +       * threaded index reads.
 +       */
 +      return !git_config_get_index_threads(&val) && val != 1;
 +}
 +
  /*
   * On success, `tempfile` is closed. If it is the temporary file
   * of a `struct lock_file`, we will therefore effectively perform
@@@ -2732,7 -2456,7 +2743,7 @@@ static int do_write_index(struct index_
  {
        uint64_t start = getnanotime();
        int newfd = tempfile->fd;
 -      git_hash_ctx c;
 +      git_hash_ctx c, eoie_c;
        struct cache_header hdr;
        int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = istate->drop_cache_tree;
 +      off_t offset;
 +      int ieot_entries = 1;
 +      struct index_entry_offset_table *ieot = NULL;
 +      int nr, nr_threads;
  
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
  
 +      if (!HAVE_THREADS || git_config_get_index_threads(&nr_threads))
 +              nr_threads = 1;
 +
 +      if (nr_threads != 1 && record_ieot()) {
 +              int ieot_blocks, cpus;
 +
 +              /*
 +               * ensure default number of ieot blocks maps evenly to the
 +               * default number of threads that will process them leaving
 +               * room for the thread to load the index extensions.
 +               */
 +              if (!nr_threads) {
 +                      ieot_blocks = istate->cache_nr / THREAD_COST;
 +                      cpus = online_cpus();
 +                      if (ieot_blocks > cpus - 1)
 +                              ieot_blocks = cpus - 1;
 +              } else {
 +                      ieot_blocks = nr_threads;
 +                      if (ieot_blocks > istate->cache_nr)
 +                              ieot_blocks = istate->cache_nr;
 +              }
 +
 +              /*
 +               * no reason to write out the IEOT extension if we don't
 +               * have enough blocks to utilize multi-threading
 +               */
 +              if (ieot_blocks > 1) {
 +                      ieot = xcalloc(1, sizeof(struct index_entry_offset_table)
 +                              + (ieot_blocks * sizeof(struct index_entry_offset)));
 +                      ieot_entries = DIV_ROUND_UP(entries, ieot_blocks);
 +              }
 +      }
 +
 +      offset = lseek(newfd, 0, SEEK_CUR);
 +      if (offset < 0) {
 +              free(ieot);
 +              return -1;
 +      }
 +      offset += write_buffer_len;
 +      nr = 0;
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
  
        for (i = 0; i < entries; i++) {
                if (ce->ce_flags & CE_REMOVE)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
 -                      ce_smudge_racily_clean_entry(ce);
 +                      ce_smudge_racily_clean_entry(istate, ce);
                if (is_null_oid(&ce->oid)) {
                        static const char msg[] = "cache entry has null sha1: %s";
                        static int allow = -1;
  
                        drop_cache_tree = 1;
                }
 +              if (ieot && i && (i % ieot_entries == 0)) {
 +                      ieot->entries[ieot->nr].nr = nr;
 +                      ieot->entries[ieot->nr].offset = offset;
 +                      ieot->nr++;
 +                      /*
 +                       * If we have a V4 index, set the first byte to an invalid
 +                       * character to ensure there is nothing common with the previous
 +                       * entry
 +                       */
 +                      if (previous_name)
 +                              previous_name->buf[0] = 0;
 +                      nr = 0;
 +                      offset = lseek(newfd, 0, SEEK_CUR);
 +                      if (offset < 0) {
 +                              free(ieot);
 +                              return -1;
 +                      }
 +                      offset += write_buffer_len;
 +              }
                if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
                        err = -1;
  
                if (err)
                        break;
 +              nr++;
 +      }
 +      if (ieot && nr) {
 +              ieot->entries[ieot->nr].nr = nr;
 +              ieot->entries[ieot->nr].offset = offset;
 +              ieot->nr++;
        }
        strbuf_release(&previous_name_buf);
  
 -      if (err)
 +      if (err) {
 +              free(ieot);
                return err;
 +      }
  
        /* Write extension data here */
 +      offset = lseek(newfd, 0, SEEK_CUR);
 +      if (offset < 0) {
 +              free(ieot);
 +              return -1;
 +      }
 +      offset += write_buffer_len;
 +      the_hash_algo->init_fn(&eoie_c);
 +
 +      /*
 +       * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
 +       * can minimize the number of extensions we have to scan through to
 +       * find it during load.  Write it out regardless of the
 +       * strip_extensions parameter as we need it when loading the shared
 +       * index.
 +       */
 +      if (ieot) {
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              write_ieot_extension(&sb, ieot);
 +              err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0
 +                      || ce_write(&c, newfd, sb.buf, sb.len) < 0;
 +              strbuf_release(&sb);
 +              free(ieot);
 +              if (err)
 +                      return -1;
 +      }
 +
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
  
                err = write_link_extension(&sb, istate) < 0 ||
 -                      write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
 +                      write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_LINK,
                                               sb.len) < 0 ||
                        ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                struct strbuf sb = STRBUF_INIT;
  
                cache_tree_write(&sb, istate->cache_tree);
 -              err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
 +              err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_TREE, sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                if (err)
                struct strbuf sb = STRBUF_INIT;
  
                resolve_undo_write(&sb, istate->resolve_undo);
 -              err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
 +              err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_RESOLVE_UNDO,
                                             sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                struct strbuf sb = STRBUF_INIT;
  
                write_untracked_extension(&sb, istate->untracked);
 -              err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED,
 +              err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_UNTRACKED,
                                             sb.len) < 0 ||
                        ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                struct strbuf sb = STRBUF_INIT;
  
                write_fsmonitor_extension(&sb, istate);
 -              err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
 +              err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
 +                      || ce_write(&c, newfd, sb.buf, sb.len) < 0;
 +              strbuf_release(&sb);
 +              if (err)
 +                      return -1;
 +      }
 +
 +      /*
 +       * CACHE_EXT_ENDOFINDEXENTRIES must be written as the last entry before the SHA1
 +       * so that it can be found and processed before all the index entries are
 +       * read.  Write it out regardless of the strip_extensions parameter as we need it
 +       * when loading the shared index.
 +       */
 +      if (offset && record_eoie()) {
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              write_eoie_extension(&sb, &eoie_c, offset);
 +              err = write_index_ext_header(&c, NULL, newfd, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
                strbuf_release(&sb);
                if (err)
@@@ -3145,9 -2754,6 +3156,9 @@@ int write_locked_index(struct index_sta
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
  
 +      if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
 +              cache_tree_verify(istate);
 +
        if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
                if (flags & COMMIT_LOCK)
                        rollback_lock_file(lock);
                struct tempfile *temp;
                int saved_errno;
  
 -              temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
 +              /* Same initial permissions as the main .git/index file */
 +              temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
                if (!temp) {
                        oidclr(&si->base_oid);
                        ret = do_write_locked_index(istate, lock, flags);
@@@ -3345,8 -2950,6 +3356,8 @@@ void move_index_extensions(struct index
  {
        dst->untracked = src->untracked;
        src->untracked = NULL;
 +      dst->cache_tree = src->cache_tree;
 +      src->cache_tree = NULL;
  }
  
  struct cache_entry *dup_cache_entry(const struct cache_entry *ce,
@@@ -3386,179 -2989,3 +3397,179 @@@ int should_validate_cache_entries(void
  
        return validate_index_cache_entries;
  }
 +
 +#define EOIE_SIZE (4 + GIT_SHA1_RAWSZ) /* <4-byte offset> + <20-byte hash> */
 +#define EOIE_SIZE_WITH_HEADER (4 + 4 + EOIE_SIZE) /* <4-byte signature> + <4-byte length> + EOIE_SIZE */
 +
 +static size_t read_eoie_extension(const char *mmap, size_t mmap_size)
 +{
 +      /*
 +       * The end of index entries (EOIE) extension is guaranteed to be last
 +       * so that it can be found by scanning backwards from the EOF.
 +       *
 +       * "EOIE"
 +       * <4-byte length>
 +       * <4-byte offset>
 +       * <20-byte hash>
 +       */
 +      const char *index, *eoie;
 +      uint32_t extsize;
 +      size_t offset, src_offset;
 +      unsigned char hash[GIT_MAX_RAWSZ];
 +      git_hash_ctx c;
 +
 +      /* ensure we have an index big enough to contain an EOIE extension */
 +      if (mmap_size < sizeof(struct cache_header) + EOIE_SIZE_WITH_HEADER + the_hash_algo->rawsz)
 +              return 0;
 +
 +      /* validate the extension signature */
 +      index = eoie = mmap + mmap_size - EOIE_SIZE_WITH_HEADER - the_hash_algo->rawsz;
 +      if (CACHE_EXT(index) != CACHE_EXT_ENDOFINDEXENTRIES)
 +              return 0;
 +      index += sizeof(uint32_t);
 +
 +      /* validate the extension size */
 +      extsize = get_be32(index);
 +      if (extsize != EOIE_SIZE)
 +              return 0;
 +      index += sizeof(uint32_t);
 +
 +      /*
 +       * Validate the offset we're going to look for the first extension
 +       * signature is after the index header and before the eoie extension.
 +       */
 +      offset = get_be32(index);
 +      if (mmap + offset < mmap + sizeof(struct cache_header))
 +              return 0;
 +      if (mmap + offset >= eoie)
 +              return 0;
 +      index += sizeof(uint32_t);
 +
 +      /*
 +       * The hash is computed over extension types and their sizes (but not
 +       * their contents).  E.g. if we have "TREE" extension that is N-bytes
 +       * long, "REUC" extension that is M-bytes long, followed by "EOIE",
 +       * then the hash would be:
 +       *
 +       * SHA-1("TREE" + <binary representation of N> +
 +       *       "REUC" + <binary representation of M>)
 +       */
 +      src_offset = offset;
 +      the_hash_algo->init_fn(&c);
 +      while (src_offset < mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) {
 +              /* After an array of active_nr index entries,
 +               * there can be arbitrary number of extended
 +               * sections, each of which is prefixed with
 +               * extension name (4-byte) and section length
 +               * in 4-byte network byte order.
 +               */
 +              uint32_t extsize;
 +              memcpy(&extsize, mmap + src_offset + 4, 4);
 +              extsize = ntohl(extsize);
 +
 +              /* verify the extension size isn't so large it will wrap around */
 +              if (src_offset + 8 + extsize < src_offset)
 +                      return 0;
 +
 +              the_hash_algo->update_fn(&c, mmap + src_offset, 8);
 +
 +              src_offset += 8;
 +              src_offset += extsize;
 +      }
 +      the_hash_algo->final_fn(hash, &c);
 +      if (!hasheq(hash, (const unsigned char *)index))
 +              return 0;
 +
 +      /* Validate that the extension offsets returned us back to the eoie extension. */
 +      if (src_offset != mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER)
 +              return 0;
 +
 +      return offset;
 +}
 +
 +static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset)
 +{
 +      uint32_t buffer;
 +      unsigned char hash[GIT_MAX_RAWSZ];
 +
 +      /* offset */
 +      put_be32(&buffer, offset);
 +      strbuf_add(sb, &buffer, sizeof(uint32_t));
 +
 +      /* hash */
 +      the_hash_algo->final_fn(hash, eoie_context);
 +      strbuf_add(sb, hash, the_hash_algo->rawsz);
 +}
 +
 +#define IEOT_VERSION  (1)
 +
 +static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset)
 +{
 +       const char *index = NULL;
 +       uint32_t extsize, ext_version;
 +       struct index_entry_offset_table *ieot;
 +       int i, nr;
 +
 +       /* find the IEOT extension */
 +       if (!offset)
 +             return NULL;
 +       while (offset <= mmap_size - the_hash_algo->rawsz - 8) {
 +             extsize = get_be32(mmap + offset + 4);
 +             if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) {
 +                     index = mmap + offset + 4 + 4;
 +                     break;
 +             }
 +             offset += 8;
 +             offset += extsize;
 +       }
 +       if (!index)
 +             return NULL;
 +
 +       /* validate the version is IEOT_VERSION */
 +       ext_version = get_be32(index);
 +       if (ext_version != IEOT_VERSION) {
 +             error("invalid IEOT version %d", ext_version);
 +             return NULL;
 +       }
 +       index += sizeof(uint32_t);
 +
 +       /* extension size - version bytes / bytes per entry */
 +       nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t));
 +       if (!nr) {
 +             error("invalid number of IEOT entries %d", nr);
 +             return NULL;
 +       }
 +       ieot = xmalloc(sizeof(struct index_entry_offset_table)
 +             + (nr * sizeof(struct index_entry_offset)));
 +       ieot->nr = nr;
 +       for (i = 0; i < nr; i++) {
 +             ieot->entries[i].offset = get_be32(index);
 +             index += sizeof(uint32_t);
 +             ieot->entries[i].nr = get_be32(index);
 +             index += sizeof(uint32_t);
 +       }
 +
 +       return ieot;
 +}
 +
 +static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot)
 +{
 +       uint32_t buffer;
 +       int i;
 +
 +       /* version */
 +       put_be32(&buffer, IEOT_VERSION);
 +       strbuf_add(sb, &buffer, sizeof(uint32_t));
 +
 +       /* ieot */
 +       for (i = 0; i < ieot->nr; i++) {
 +
 +             /* offset */
 +             put_be32(&buffer, ieot->entries[i].offset);
 +             strbuf_add(sb, &buffer, sizeof(uint32_t));
 +
 +             /* count */
 +             put_be32(&buffer, ieot->entries[i].nr);
 +             strbuf_add(sb, &buffer, sizeof(uint32_t));
 +       }
 +}
diff --combined submodule-config.c
index 52702c62d9e3a205c14bdd9f468509f98fe4ced1,665a67c5a03e7b9310857d2a717fbd00b16b7939..8c00855ed8fc8b1751ff64ebb53621d0e4c78f1d
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "dir.h"
  #include "repository.h"
  #include "config.h"
  #include "submodule-config.h"
@@@ -46,7 -45,7 +46,7 @@@ static int config_path_cmp(const void *
        const struct submodule_entry *b = entry_or_key;
  
        return strcmp(a->config->path, b->config->path) ||
 -             oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
 +             !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
  }
  
  static int config_name_cmp(const void *unused_cmp_data,
@@@ -58,7 -57,7 +58,7 @@@
        const struct submodule_entry *b = entry_or_key;
  
        return strcmp(a->config->name, b->config->name) ||
 -             oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
 +             !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
  }
  
  static struct submodule_cache *submodule_cache_alloc(void)
@@@ -398,6 -397,13 +398,13 @@@ struct parse_config_parameter 
        int overwrite;
  };
  
+ /*
+  * Parse a config item from .gitmodules.
+  *
+  * This does not handle submodule-related configuration from the main
+  * config store (.git/config, etc).  Callers are responsible for
+  * checking for overrides in the main config store when appropriate.
+  */
  static int parse_config(const char *var, const char *value, void *data)
  {
        struct parse_config_parameter *me = data;
                        warn_multiple_config(me->treeish_name, submodule->name,
                                             "update");
                else if (parse_submodule_update_strategy(value,
-                        &submodule->update_strategy) < 0)
-                               die(_("invalid value for %s"), var);
+                        &submodule->update_strategy) < 0 ||
+                        submodule->update_strategy.type == SM_UPDATE_COMMAND)
+                       die(_("invalid value for %s"), var);
        } else if (!strcmp(item.buf, "shallow")) {
                if (!me->overwrite && submodule->recommend_shallow != -1)
                        warn_multiple_config(me->treeish_name, submodule->name,
@@@ -614,34 -621,8 +622,34 @@@ static void submodule_cache_check_init(
  static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
  {
        if (repo->worktree) {
 -              char *file = repo_worktree_path(repo, GITMODULES_FILE);
 -              git_config_from_file(fn, file, data);
 +              struct git_config_source config_source = { 0 };
 +              const struct config_options opts = { 0 };
 +              struct object_id oid;
 +              char *file;
 +
 +              file = repo_worktree_path(repo, GITMODULES_FILE);
 +              if (file_exists(file)) {
 +                      config_source.file = file;
 +              } else if (repo->submodule_prefix) {
 +                      /*
 +                       * When get_oid and config_with_options, used below,
 +                       * become able to work on a specific repository, this
 +                       * warning branch can be removed.
 +                       */
 +                      warning("nested submodules without %s in the working tree are not supported yet",
 +                              GITMODULES_FILE);
 +                      goto out;
 +              } else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
 +                      config_source.blob = GITMODULES_INDEX;
 +              } else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
 +                      config_source.blob = GITMODULES_HEAD;
 +              } else {
 +                      goto out;
 +              }
 +
 +              config_with_options(fn, data, &config_source, &opts);
 +
 +out:
                free(file);
        }
  }
@@@ -719,43 -700,6 +727,43 @@@ void submodule_free(struct repository *
                submodule_cache_clear(r->submodule_cache);
  }
  
 +static int config_print_callback(const char *var, const char *value, void *cb_data)
 +{
 +      char *wanted_key = cb_data;
 +
 +      if (!strcmp(wanted_key, var))
 +              printf("%s\n", value);
 +
 +      return 0;
 +}
 +
 +int print_config_from_gitmodules(struct repository *repo, const char *key)
 +{
 +      int ret;
 +      char *store_key;
 +
 +      ret = git_config_parse_key(key, &store_key, NULL);
 +      if (ret < 0)
 +              return CONFIG_INVALID_KEY;
 +
 +      config_from_gitmodules(config_print_callback, repo, store_key);
 +
 +      free(store_key);
 +      return 0;
 +}
 +
 +int config_set_in_gitmodules_file_gently(const char *key, const char *value)
 +{
 +      int ret;
 +
 +      ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
 +      if (ret < 0)
 +              /* Maybe the user already did that, don't error out here */
 +              warning(_("Could not update .gitmodules entry %s"), key);
 +
 +      return ret;
 +}
 +
  struct fetch_config {
        int *max_children;
        int *recurse_submodules;
diff --combined submodule.c
index 6415cc55807c7ed9ee4cbcaa57f41a5804b6e854,9f87af40b339371cceff4c6b4fff7996b76c5f77..37d29bb252474e43f1a942a0d19e39697ed1ff21
@@@ -22,7 -22,6 +22,7 @@@
  #include "worktree.h"
  #include "parse-options.h"
  #include "object-store.h"
 +#include "commit-reach.h"
  
  static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
  static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
@@@ -51,24 -50,6 +51,24 @@@ int is_gitmodules_unmerged(const struc
        return 0;
  }
  
 +/*
 + * Check if the .gitmodules file is safe to write.
 + *
 + * Writing to the .gitmodules file requires that the file exists in the
 + * working tree or, if it doesn't, that a brand new .gitmodules file is going
 + * to be created (i.e. it's neither in the index nor in the current branch).
 + *
 + * It is not safe to write to .gitmodules if it's not in the working tree but
 + * it is in the index or in the current branch, because writing new values
 + * (and staging them) would blindly overwrite ALL the old content.
 + */
 +int is_writing_gitmodules_ok(void)
 +{
 +      struct object_id oid;
 +      return file_exists(GITMODULES_FILE) ||
 +              (get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
 +}
 +
  /*
   * Check if the .gitmodules file has unstaged modifications.  This must be
   * checked before allowing modifications to the .gitmodules file with the
@@@ -107,7 -88,6 +107,7 @@@ int update_path_in_gitmodules(const cha
  {
        struct strbuf entry = STRBUF_INIT;
        const struct submodule *submodule;
 +      int ret;
  
        if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
                return -1;
        strbuf_addstr(&entry, "submodule.");
        strbuf_addstr(&entry, submodule->name);
        strbuf_addstr(&entry, ".path");
 -      if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
 -              /* Maybe the user already did that, don't error out here */
 -              warning(_("Could not update .gitmodules entry %s"), entry.buf);
 -              strbuf_release(&entry);
 -              return -1;
 -      }
 +      ret = config_set_in_gitmodules_file_gently(entry.buf, newpath);
        strbuf_release(&entry);
 -      return 0;
 +      return ret;
  }
  
  /*
@@@ -442,7 -427,7 +442,7 @@@ static int prepare_submodule_summary(st
  {
        struct commit_list *list;
  
 -      init_revisions(rev, NULL);
 +      repo_init_revisions(the_repository, rev, NULL);
        setup_revisions(0, NULL, rev, NULL);
        rev->left_right = 1;
        rev->first_parent_only = 1;
@@@ -550,7 -535,7 +550,7 @@@ static void show_submodule_header(struc
                        fast_backward = 1;
        }
  
 -      if (!oidcmp(one, two)) {
 +      if (oideq(one, two)) {
                strbuf_release(&sb);
                return;
        }
@@@ -708,7 -693,6 +708,7 @@@ static struct oid_array *submodule_comm
  }
  
  struct collect_changed_submodules_cb_data {
 +      struct repository *repo;
        struct string_list *changed;
        const struct object_id *commit_oid;
  };
@@@ -748,7 -732,7 +748,7 @@@ static void collect_changed_submodules_
                if (!S_ISGITLINK(p->two->mode))
                        continue;
  
 -              submodule = submodule_from_path(the_repository,
 +              submodule = submodule_from_path(me->repo,
                                                commit_oid, p->two->path);
                if (submodule)
                        name = submodule->name;
                        name = default_name_or_path(p->two->path);
                        /* make sure name does not collide with existing one */
                        if (name)
 -                              submodule = submodule_from_name(the_repository,
 +                              submodule = submodule_from_name(me->repo,
                                                                commit_oid, name);
                        if (submodule) {
                                warning("Submodule in commit %s at path: "
   * have a corresponding 'struct oid_array' (in the 'util' field) which lists
   * what the submodule pointers were updated to during the change.
   */
 -static void collect_changed_submodules(struct string_list *changed,
 +static void collect_changed_submodules(struct repository *r,
 +                                     struct string_list *changed,
                                       struct argv_array *argv)
  {
        struct rev_info rev;
        const struct commit *commit;
  
 -      init_revisions(&rev, NULL);
 +      repo_init_revisions(r, &rev, NULL);
        setup_revisions(argv->argc, argv->argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
        while ((commit = get_revision(&rev))) {
                struct rev_info diff_rev;
                struct collect_changed_submodules_cb_data data;
 +              data.repo = r;
                data.changed = changed;
                data.commit_oid = &commit->object.oid;
  
 -              init_revisions(&diff_rev, NULL);
 +              repo_init_revisions(r, &diff_rev, NULL);
                diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
                diff_rev.diffopt.format_callback_data = &data;
@@@ -832,7 -814,6 +832,7 @@@ static int append_oid_to_argv(const str
  }
  
  struct has_commit_data {
 +      struct repository *repo;
        int result;
        const char *path;
  };
@@@ -841,7 -822,7 +841,7 @@@ static int check_has_commit(const struc
  {
        struct has_commit_data *cb = data;
  
 -      enum object_type type = oid_object_info(the_repository, oid, NULL);
 +      enum object_type type = oid_object_info(cb->repo, oid, NULL);
  
        switch (type) {
        case OBJ_COMMIT:
        }
  }
  
 -static int submodule_has_commits(const char *path, struct oid_array *commits)
 +static int submodule_has_commits(struct repository *r,
 +                               const char *path,
 +                               struct oid_array *commits)
  {
 -      struct has_commit_data has_commit = { 1, path };
 +      struct has_commit_data has_commit = { r, 1, path };
  
        /*
         * Perform a cheap, but incorrect check for the existence of 'commits'.
        return has_commit.result;
  }
  
 -static int submodule_needs_pushing(const char *path, struct oid_array *commits)
 +static int submodule_needs_pushing(struct repository *r,
 +                                 const char *path,
 +                                 struct oid_array *commits)
  {
 -      if (!submodule_has_commits(path, commits))
 +      if (!submodule_has_commits(r, path, commits))
                /*
                 * NOTE: We do consider it safe to return "no" here. The
                 * correct answer would be "We do not know" instead of
        return 0;
  }
  
 -int find_unpushed_submodules(struct oid_array *commits,
 -              const char *remotes_name, struct string_list *needs_pushing)
 +int find_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
 +                           const char *remotes_name,
 +                           struct string_list *needs_pushing)
  {
        struct string_list submodules = STRING_LIST_INIT_DUP;
        struct string_list_item *name;
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
  
 -      collect_changed_submodules(&submodules, &argv);
 +      collect_changed_submodules(r, &submodules, &argv);
  
        for_each_string_list_item(name, &submodules) {
                struct oid_array *commits = name->util;
                const struct submodule *submodule;
                const char *path = NULL;
  
 -              submodule = submodule_from_name(the_repository, &null_oid, name->string);
 +              submodule = submodule_from_name(r, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
                if (!path)
                        continue;
  
 -              if (submodule_needs_pushing(path, commits))
 +              if (submodule_needs_pushing(r, path, commits))
                        string_list_insert(needs_pushing, path);
        }
  
@@@ -1068,8 -1043,7 +1068,8 @@@ static void submodule_push_check(const 
                die("process for submodule '%s' failed", path);
  }
  
 -int push_unpushed_submodules(struct oid_array *commits,
 +int push_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
  
 -      if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
 +      if (!find_unpushed_submodules(r, commits,
 +                                    remote->name, &needs_pushing))
                return 1;
  
        /*
@@@ -1136,14 -1109,14 +1136,14 @@@ void check_for_new_submodule_commits(st
        oid_array_append(&ref_tips_after_fetch, oid);
  }
  
 -static void calculate_changed_submodule_paths(void)
 +static void calculate_changed_submodule_paths(struct repository *r)
  {
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list changed_submodules = STRING_LIST_INIT_DUP;
        const struct string_list_item *name;
  
        /* No need to check if there are no submodules configured */
 -      if (!submodule_from_path(the_repository, NULL, NULL))
 +      if (!submodule_from_path(r, NULL, NULL))
                return;
  
        argv_array_push(&argv, "--"); /* argv[0] program name */
         * Collect all submodules (whether checked out or not) for which new
         * commits have been recorded upstream in "changed_submodule_names".
         */
 -      collect_changed_submodules(&changed_submodules, &argv);
 +      collect_changed_submodules(r, &changed_submodules, &argv);
  
        for_each_string_list_item(name, &changed_submodules) {
                struct oid_array *commits = name->util;
                const struct submodule *submodule;
                const char *path = NULL;
  
 -              submodule = submodule_from_name(the_repository, &null_oid, name->string);
 +              submodule = submodule_from_name(r, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
                if (!path)
                        continue;
  
 -              if (!submodule_has_commits(path, commits))
 +              if (!submodule_has_commits(r, path, commits))
                        string_list_append(&changed_submodule_names, name->string);
        }
  
        initialized_fetch_ref_tips = 0;
  }
  
 -int submodule_touches_in_range(struct object_id *excl_oid,
 +int submodule_touches_in_range(struct repository *r,
 +                             struct object_id *excl_oid,
                               struct object_id *incl_oid)
  {
        struct string_list subs = STRING_LIST_INIT_DUP;
        int ret;
  
        /* No need to check if there are no submodules configured */
 -      if (!submodule_from_path(the_repository, NULL, NULL))
 +      if (!submodule_from_path(r, NULL, NULL))
                return 0;
  
        argv_array_push(&args, "--"); /* args[0] program name */
                argv_array_push(&args, oid_to_hex(excl_oid));
        }
  
 -      collect_changed_submodules(&subs, &args);
 +      collect_changed_submodules(r, &subs, &args);
        ret = subs.nr;
  
        argv_array_clear(&args);
@@@ -1373,7 -1345,7 +1373,7 @@@ int fetch_populated_submodules(struct r
        argv_array_push(&spf.args, "--recurse-submodules-default");
        /* default value, "--submodule-prefix" and its value are added later */
  
 -      calculate_changed_submodule_paths();
 +      calculate_changed_submodule_paths(r);
        run_processes_parallel(max_parallel_jobs,
                               get_next_submodule,
                               fetch_start_failure,
        return ret;
  }
  
+ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
+ {
+       size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
+       char *p;
+       int ret = 0;
+       if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
+           strcmp(p, submodule_name))
+               BUG("submodule name '%s' not a suffix of git dir '%s'",
+                   submodule_name, git_dir);
+       /*
+        * We prevent the contents of sibling submodules' git directories to
+        * clash.
+        *
+        * Example: having a submodule named `hippo` and another one named
+        * `hippo/hooks` would result in the git directories
+        * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
+        * but the latter directory is already designated to contain the hooks
+        * of the former.
+        */
+       for (; *p; p++) {
+               if (is_dir_sep(*p)) {
+                       char c = *p;
+                       *p = '\0';
+                       if (is_git_directory(git_dir))
+                               ret = -1;
+                       *p = c;
+                       if (ret < 0)
+                               return error(_("submodule git dir '%s' is "
+                                              "inside git dir '%.*s'"),
+                                            git_dir,
+                                            (int)(p - git_dir), git_dir);
+               }
+       }
+       return 0;
+ }
  /*
   * Embeds a single submodules git directory into the superprojects git dir,
   * non recursively.
@@@ -1740,7 -1753,7 +1781,7 @@@ static void relocate_single_git_dir_int
                                                      const char *path)
  {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
-       const char *new_git_dir;
+       char *new_git_dir;
        const struct submodule *sub;
  
        if (submodule_uses_worktrees(path))
        if (!sub)
                die(_("could not lookup name for submodule '%s'"), path);
  
-       new_git_dir = git_path("modules/%s", sub->name);
+       new_git_dir = git_pathdup("modules/%s", sub->name);
+       if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
+               die(_("refusing to move '%s' into an existing git dir"),
+                   real_old_git_dir);
        if (safe_create_leading_directories_const(new_git_dir) < 0)
                die(_("could not create directory '%s'"), new_git_dir);
        real_new_git_dir = real_pathdup(new_git_dir, 1);
+       free(new_git_dir);
  
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
                get_super_prefix_or_empty(), path,
diff --combined submodule.h
index a680214c01a5fda90a21af547389f6333388683c,faa586b94125f63e514a114bf6b989c11538ff4f..ac206dc182a8e65d8fa066ece747593bf8b781ea
@@@ -40,7 -40,6 +40,7 @@@ struct submodule_update_strategy 
  #define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
  
  int is_gitmodules_unmerged(const struct index_state *istate);
 +int is_writing_gitmodules_ok(void);
  int is_staging_gitmodules_ok(struct index_state *istate);
  int update_path_in_gitmodules(const char *oldpath, const char *newpath);
  int remove_path_from_gitmodules(const char *path);
@@@ -103,16 -102,13 +103,16 @@@ int add_submodule_odb(const char *path)
   * Checks if there are submodule changes in a..b. If a is the null OID,
   * checks b and all its ancestors instead.
   */
 -int submodule_touches_in_range(struct object_id *a,
 +int submodule_touches_in_range(struct repository *r,
 +                             struct object_id *a,
                               struct object_id *b);
 -int find_unpushed_submodules(struct oid_array *commits,
 +int find_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const char *remotes_name,
                             struct string_list *needs_pushing);
  struct refspec;
 -int push_unpushed_submodules(struct oid_array *commits,
 +int push_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
   */
  int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
  
+ /*
+  * Make sure that no submodule's git dir is nested in a sibling submodule's.
+  */
+ int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
  #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
  #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
  int submodule_move_head(const char *path,
diff --combined t/t0060-path-utils.sh
index c7b53e494ba43fdcff874a506468a4ecc0881a48,ddf1a9a08b0288f67ed7845a3bfb0a1d30822aea..b193ed42050509bc3951e2faae22fbecf0d0b044
@@@ -165,6 -165,15 +165,15 @@@ test_expect_success 'absolute path reje
        test_must_fail test-tool path-utils absolute_path ""
  '
  
+ test_expect_success MINGW '<drive-letter>:\\abc is an absolute path' '
+       for letter in : \" C Z 1 ä
+       do
+               path=$letter:\\abc &&
+               absolute="$(test-tool path-utils absolute_path "$path")" &&
+               test "$path" = "$absolute" || return 1
+       done
+ '
  test_expect_success 'real path rejects the empty string' '
        test_must_fail test-tool path-utils real_path ""
  '
@@@ -306,8 -315,6 +315,8 @@@ test_git_path GIT_COMMON_DIR=bar hooks/
  test_git_path GIT_COMMON_DIR=bar config                   bar/config
  test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
  test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
 +test_git_path GIT_COMMON_DIR=bar common                   bar/common
 +test_git_path GIT_COMMON_DIR=bar common/file              bar/common/file
  
  # In the tests below, $(pwd) must be used because it is a native path on
  # Windows and avoids MSYS's path mangling (which simplifies "foo/../bar" and
@@@ -423,6 -430,9 +432,9 @@@ test_expect_success 'match .gitmodules
                ~1000000 \
                ~9999999 \
                \
+               .gitmodules:\$DATA \
+               "gitmod~4 . :\$DATA" \
+               \
                --not \
                ".gitmodules x"  \
                ".gitmodules .x" \
                \
                GI7EB~1 \
                GI7EB~01 \
-               GI7EB~1X
+               GI7EB~1X \
+               \
+               .gitmodules,:\$DATA
+ '
+ test_expect_success MINGW 'is_valid_path() on Windows' '
+        test-tool path-utils is_valid_path \
+               win32 \
+               "win32 x" \
+               ../hello.txt \
+               C:\\git \
+               \
+               --not \
+               "win32 "  \
+               "win32 /x "  \
+               "win32."  \
+               "win32 . ." \
+               .../hello.txt \
+               colon:test
  '
  
  test_done
diff --combined t/t1450-fsck.sh
index e20e8fa8301607b94cff7c42e83545f072d5191f,f00fea6aadc84b61a24bcf87b0fef3b9b6ae0fed..c767e2783b50fce0f60254b1b7e1b27a82587eea
@@@ -101,41 -101,6 +101,41 @@@ test_expect_success 'HEAD link pointin
        grep "HEAD points to something strange" out
  '
  
 +test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
 +      test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
 +      test_when_finished "rm -rf .git/worktrees wt" &&
 +      git worktree add wt &&
 +      mv .git/HEAD .git/SAVED_HEAD &&
 +      echo $ZERO_OID >.git/HEAD &&
 +      # avoid corrupt/broken HEAD from interfering with repo discovery
 +      test_must_fail git -C wt fsck 2>out &&
 +      grep "main-worktree/HEAD: detached HEAD points" out
 +'
 +
 +test_expect_success 'other worktree HEAD link pointing at a funny object' '
 +      test_when_finished "rm -rf .git/worktrees other" &&
 +      git worktree add other &&
 +      echo $ZERO_OID >.git/worktrees/other/HEAD &&
 +      test_must_fail git fsck 2>out &&
 +      grep "worktrees/other/HEAD: detached HEAD points" out
 +'
 +
 +test_expect_success 'other worktree HEAD link pointing at missing object' '
 +      test_when_finished "rm -rf .git/worktrees other" &&
 +      git worktree add other &&
 +      echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
 +      test_must_fail git fsck 2>out &&
 +      grep "worktrees/other/HEAD: invalid sha1 pointer" out
 +'
 +
 +test_expect_success 'other worktree HEAD link pointing at a funny place' '
 +      test_when_finished "rm -rf .git/worktrees other" &&
 +      git worktree add other &&
 +      echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
 +      test_must_fail git fsck 2>out &&
 +      grep "worktrees/other/HEAD points to something strange" out
 +'
 +
  test_expect_success 'email without @ is okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@/AT/" basis >okay &&
@@@ -453,6 -418,7 +453,7 @@@ while read name path pretty; d
                (
                        git init $name-$type &&
                        cd $name-$type &&
+                       git config core.protectNTFS false &&
                        echo content >file &&
                        git add file &&
                        git commit -m base &&
index e87164aa8ffdba169ba6f91af99ae3d28f28459b,6a68c472ee3d6699b66d5acb92e244ada534b962..ad7d8fa69e52046a653ba5b3789ce2cbc9d66a2c
@@@ -407,12 -407,26 +407,26 @@@ test_expect_success 'submodule update 
        )
  '
  
- test_expect_success 'submodule update - command in .gitmodules is ignored' '
+ test_expect_success 'submodule update - command in .gitmodules is rejected' '
        test_when_finished "git -C super reset --hard HEAD^" &&
        git -C super config -f .gitmodules submodule.submodule.update "!false" &&
        git -C super commit -a -m "add command to .gitmodules file" &&
        git -C super/submodule reset --hard $submodulesha1^ &&
-       git -C super submodule update submodule
+       test_must_fail git -C super submodule update submodule
+ '
+ test_expect_success 'fsck detects command in .gitmodules' '
+       git init command-in-gitmodules &&
+       (
+               cd command-in-gitmodules &&
+               git submodule add ../submodule submodule &&
+               test_commit adding-submodule &&
+               git config -f .gitmodules submodule.submodule.update "!false" &&
+               git add .gitmodules &&
+               test_commit configuring-update &&
+               test_must_fail git fsck
+       )
  '
  
  cat << EOF >expect
@@@ -481,6 -495,9 +495,9 @@@ test_expect_success 'recursive submodul
  '
  
  test_expect_success 'submodule init does not copy command into .git/config' '
+       test_when_finished "git -C super update-index --force-remove submodule1" &&
+       test_when_finished git config -f super/.gitmodules \
+               --remove-section submodule.submodule1 &&
        (cd super &&
         git ls-files -s submodule >out &&
         H=$(cut -d" " -f2 out) &&
         git config -f .gitmodules submodule.submodule1.path submodule1 &&
         git config -f .gitmodules submodule.submodule1.url ../submodule &&
         git config -f .gitmodules submodule.submodule1.update !false &&
-        git submodule init submodule1 &&
-        echo "none" >expect &&
-        git config submodule.submodule1.update >actual &&
-        test_cmp expect actual
+        test_must_fail git submodule init submodule1 &&
+        test_expect_code 1 git config submodule.submodule1.update >actual &&
+        test_must_be_empty actual
        )
  '
  
@@@ -789,7 -805,7 +805,7 @@@ test_expect_success 'submodule add plac
         (cd .git/modules/deeper/submodule &&
          git log > ../../../../actual
         ) &&
 -       test_cmp actual expected
 +       test_cmp expected actual
        )
  '
  
@@@ -807,7 -823,7 +823,7 @@@ test_expect_success 'submodule update p
         (cd .git/modules/deeper/submodule &&
          git log > ../../../../actual
         ) &&
 -       test_cmp actual expected
 +       test_cmp expected actual
        )
  '
  
@@@ -827,7 -843,7 +843,7 @@@ test_expect_success 'submodule add plac
         git add deeper/submodule &&
         git commit -m "update submodule" &&
         git push origin : &&
 -       test_cmp actual expected
 +       test_cmp expected actual
        )
  '
  
@@@ -874,7 -890,7 +890,7 @@@ test_expect_success 'submodule update p
         (cd .git/modules/submodule/modules/subsubmodule &&
          git log > ../../../../../actual
         ) &&
 -       test_cmp actual expected
 +       test_cmp expected actual
        )
  '
  
diff --combined t/t9300-fast-import.sh
index 59a13b6a779b437fa377b30b2e0856f285f806b6,5d69466f729611b873978b06fb4099e0be25147e..377c2b495833ac61f49d0933fcf4719d05fdd8dc
@@@ -1558,7 -1558,7 +1558,7 @@@ test_expect_success 'O: blank lines no
        INPUT_END
  
        git fast-import <input &&
 -      test 8 = $(find .git/objects/pack -type f | wc -l) &&
 +      test 8 = $(find .git/objects/pack -type f | grep -v multi-pack-index | wc -l) &&
        test $(git rev-parse refs/tags/O3-2nd) = $(git rev-parse O3^) &&
        git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
        test_cmp expect actual
@@@ -2106,12 -2106,27 +2106,27 @@@ test_expect_success 'R: abort on receiv
        test_must_fail git fast-import <input
  '
  
+ test_expect_success 'R: import-marks features forbidden by default' '
+       >git.marks &&
+       echo "feature import-marks=git.marks" >input &&
+       test_must_fail git fast-import <input &&
+       echo "feature import-marks-if-exists=git.marks" >input &&
+       test_must_fail git fast-import <input
+ '
  test_expect_success 'R: only one import-marks feature allowed per stream' '
+       >git.marks &&
+       >git2.marks &&
        cat >input <<-EOF &&
        feature import-marks=git.marks
        feature import-marks=git2.marks
        EOF
  
+       test_must_fail git fast-import --allow-unsafe-features <input
+ '
+ test_expect_success 'R: export-marks feature forbidden by default' '
+       echo "feature export-marks=git.marks" >input &&
        test_must_fail git fast-import <input
  '
  
@@@ -2125,19 -2140,29 +2140,29 @@@ test_expect_success 'R: export-marks fe
  
        EOF
  
-       cat input | git fast-import &&
+       git fast-import --allow-unsafe-features <input &&
        grep :1 git.marks
  '
  
  test_expect_success 'R: export-marks options can be overridden by commandline options' '
-       cat input | git fast-import --export-marks=other.marks &&
-       grep :1 other.marks
+       cat >input <<-\EOF &&
+       feature export-marks=feature-sub/git.marks
+       blob
+       mark :1
+       data 3
+       hi
+       EOF
+       git fast-import --allow-unsafe-features \
+                       --export-marks=cmdline-sub/other.marks <input &&
+       grep :1 cmdline-sub/other.marks &&
+       test_path_is_missing feature-sub
  '
  
  test_expect_success 'R: catch typo in marks file name' '
        test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
        echo "feature import-marks=nonexistent.marks" |
-       test_must_fail git fast-import
+       test_must_fail git fast-import --allow-unsafe-features
  '
  
  test_expect_success 'R: import and output marks can be the same file' '
@@@ -2192,7 -2217,8 +2217,8 @@@ test_expect_success 'R: --import-marks-
  test_expect_success 'R: feature import-marks-if-exists' '
        rm -f io.marks &&
  
-       git fast-import --export-marks=io.marks <<-\EOF &&
+       git fast-import --export-marks=io.marks \
+                       --allow-unsafe-features <<-\EOF &&
        feature import-marks-if-exists=not_io.marks
        EOF
        test_must_be_empty io.marks &&
        echo ":1 $blob" >expect &&
        echo ":2 $blob" >>expect &&
  
-       git fast-import --export-marks=io.marks <<-\EOF &&
+       git fast-import --export-marks=io.marks \
+                       --allow-unsafe-features <<-\EOF &&
        feature import-marks-if-exists=io.marks
        blob
        mark :2
        echo ":3 $blob" >>expect &&
  
        git fast-import --import-marks=io.marks \
-                       --export-marks=io.marks <<-\EOF &&
+                       --export-marks=io.marks \
+                       --allow-unsafe-features <<-\EOF &&
        feature import-marks-if-exists=not_io.marks
        blob
        mark :3
        test_cmp expect io.marks &&
  
        git fast-import --import-marks-if-exists=not_io.marks \
-                       --export-marks=io.marks <<-\EOF &&
+                       --export-marks=io.marks \
+                       --allow-unsafe-features <<-\EOF &&
        feature import-marks-if-exists=io.marks
        EOF
        test_must_be_empty io.marks
@@@ -2239,7 -2268,7 +2268,7 @@@ test_expect_success 'R: import to outpu
        feature export-marks=marks.new
        EOF
  
-       cat input | git fast-import &&
+       git fast-import --allow-unsafe-features <input &&
        test_cmp marks.out marks.new
  '
  
@@@ -2249,7 -2278,7 +2278,7 @@@ test_expect_success 'R: import marks pr
        feature export-marks=marks.new
        EOF
  
-       cat input | git fast-import --import-marks=marks.out &&
+       git fast-import --import-marks=marks.out --allow-unsafe-features <input &&
        test_cmp marks.out marks.new
  '
  
@@@ -2262,7 -2291,8 +2291,8 @@@ test_expect_success 'R: multiple --impo
  
        head -n2 marks.out > one.marks &&
        tail -n +3 marks.out > two.marks &&
-       git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
+       git fast-import --import-marks=one.marks --import-marks=two.marks \
+               --allow-unsafe-features <input &&
        test_cmp marks.out combined.marks
  '
  
@@@ -2275,7 -2305,7 +2305,7 @@@ test_expect_success 'R: feature relativ
  
        mkdir -p .git/info/fast-import/ &&
        cp marks.new .git/info/fast-import/relative.in &&
-       git fast-import <input &&
+       git fast-import --allow-unsafe-features <input &&
        test_cmp marks.new .git/info/fast-import/relative.out
  '
  
@@@ -2287,7 -2317,7 +2317,7 @@@ test_expect_success 'R: feature no-rela
        feature export-marks=non-relative.out
        EOF
  
-       git fast-import <input &&
+       git fast-import --allow-unsafe-features <input &&
        test_cmp marks.new non-relative.out
  '
  
@@@ -2557,7 -2587,7 +2587,7 @@@ test_expect_success 'R: quiet option re
  
        EOF
  
-       cat input | git fast-import 2> output &&
+       git fast-import 2>output <input &&
        test_must_be_empty output
  '
  
diff --combined transport-helper.c
index bf225c698fac81a9a94eff6d3371988ac4ff0bac,1734ec02c1ffb53b71938f942155fb8b5832983c..f79221cc9495b8766f190234377b0a113f6f1d70
@@@ -423,6 -423,7 +423,7 @@@ static int get_importer(struct transpor
        child_process_init(fastimport);
        fastimport->in = helper->out;
        argv_array_push(&fastimport->args, "fast-import");
+       argv_array_push(&fastimport->args, "--allow-unsafe-features");
        argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
  
        if (data->bidi_import) {
@@@ -573,7 -574,7 +574,7 @@@ static int run_connect(struct transpor
                        fprintf(stderr, "Debug: Falling back to dumb "
                                "transport.\n");
        } else {
 -              die(_(_("unknown response to connect: %s")),
 +              die(_("unknown response to connect: %s"),
                    cmdbuf->buf);
        }
  
@@@ -1105,7 -1106,6 +1106,7 @@@ static struct ref *get_refs_list(struc
  }
  
  static struct transport_vtable vtable = {
 +      0,
        set_helper_option,
        get_refs_list,
        fetch,
diff --combined tree-walk.c
index 79bafbd1a23c4a9e20ec623c084778904c534be7,9b96b7e6b9ed0e114f100185e6f44d17191f7271..bf07946ec49cf1fc0c25f8a9f46b1e48e1b9635e
@@@ -43,6 -43,12 +43,12 @@@ static int decode_tree_entry(struct tre
                strbuf_addstr(err, _("empty filename in tree entry"));
                return -1;
        }
+ #ifdef GIT_WINDOWS_NATIVE
+       if (protect_ntfs && strchr(path, '\\')) {
+               strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
+               return -1;
+       }
+ #endif
        len = strlen(path) + 1;
  
        /* Initialize the descriptor entry */
@@@ -1107,7 -1113,7 +1113,7 @@@ enum interesting tree_entry_interesting
         *   5  |  file |    1     |    1     |   0
         *   6  |  file |    1     |    2     |   0
         *   7  |  file |    2     |   -1     |   2
 -       *   8  |  file |    2     |    0     |   2
 +       *   8  |  file |    2     |    0     |   1
         *   9  |  file |    2     |    1     |   0
         *  10  |  file |    2     |    2     |  -1
         * -----+-------+----------+----------+-------
         *  15  |  dir  |    1     |    1     |   1 (*)
         *  16  |  dir  |    1     |    2     |   0
         *  17  |  dir  |    2     |   -1     |   2
 -       *  18  |  dir  |    2     |    0     |   2
 +       *  18  |  dir  |    2     |    0     |   1
         *  19  |  dir  |    2     |    1     |   1 (*)
         *  20  |  dir  |    2     |    2     |  -1
         *
  
        negative = do_match(entry, base, base_offset, ps, 1);
  
 -      /* #3, #4, #7, #8, #13, #14, #17, #18 */
 +      /* #8, #18 */
 +      if (positive == all_entries_interesting &&
 +          negative == entry_not_interesting)
 +              return entry_interesting;
 +
 +      /* #3, #4, #7, #13, #14, #17 */
        if (negative <= entry_not_interesting)
                return positive;
  
diff --combined unpack-trees.c
index 7570df481bf69824e4b163a6c7a15985b72d1326,705054a66950d2a42fe9c8f33f71c3e5d90910b9..545d5668fdf79015784cafcfbc24322b68e8927c
@@@ -336,64 -336,20 +336,64 @@@ static struct progress *get_progress(st
        return start_delayed_progress(_("Checking out files"), total);
  }
  
 +static void setup_collided_checkout_detection(struct checkout *state,
 +                                            struct index_state *index)
 +{
 +      int i;
 +
 +      state->clone = 1;
 +      for (i = 0; i < index->cache_nr; i++)
 +              index->cache[i]->ce_flags &= ~CE_MATCHED;
 +}
 +
 +static void report_collided_checkout(struct index_state *index)
 +{
 +      struct string_list list = STRING_LIST_INIT_NODUP;
 +      int i;
 +
 +      for (i = 0; i < index->cache_nr; i++) {
 +              struct cache_entry *ce = index->cache[i];
 +
 +              if (!(ce->ce_flags & CE_MATCHED))
 +                      continue;
 +
 +              string_list_append(&list, ce->name);
 +              ce->ce_flags &= ~CE_MATCHED;
 +      }
 +
 +      list.cmp = fspathcmp;
 +      string_list_sort(&list);
 +
 +      if (list.nr) {
 +              warning(_("the following paths have collided (e.g. case-sensitive paths\n"
 +                        "on a case-insensitive filesystem) and only one from the same\n"
 +                        "colliding group is in the working tree:\n"));
 +
 +              for (i = 0; i < list.nr; i++)
 +                      fprintf(stderr, "  '%s'\n", list.items[i].string);
 +      }
 +
 +      string_list_clear(&list, 0);
 +}
 +
  static int check_updates(struct unpack_trees_options *o)
  {
        unsigned cnt = 0;
        int errs = 0;
 -      struct progress *progress = NULL;
 +      struct progress *progress;
        struct index_state *index = &o->result;
        struct checkout state = CHECKOUT_INIT;
        int i;
  
 +      trace_performance_enter();
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
        state.istate = index;
  
 +      if (o->clone)
 +              setup_collided_checkout_detection(&state, index);
 +
        progress = get_progress(o);
  
        if (o->update)
        errs |= finish_delayed_checkout(&state);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN);
 +
 +      if (o->clone)
 +              report_collided_checkout(index);
 +
 +      trace_performance_leave("check_updates");
        return errs != 0;
  }
  
@@@ -679,114 -630,7 +679,114 @@@ static int switch_cache_bottom(struct t
  
  static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
  {
 -      return name_j->oid && name_k->oid && !oidcmp(name_j->oid, name_k->oid);
 +      return name_j->oid && name_k->oid && oideq(name_j->oid, name_k->oid);
 +}
 +
 +static int all_trees_same_as_cache_tree(int n, unsigned long dirmask,
 +                                      struct name_entry *names,
 +                                      struct traverse_info *info)
 +{
 +      struct unpack_trees_options *o = info->data;
 +      int i;
 +
 +      if (!o->merge || dirmask != ((1 << n) - 1))
 +              return 0;
 +
 +      for (i = 1; i < n; i++)
 +              if (!are_same_oid(names, names + i))
 +                      return 0;
 +
 +      return cache_tree_matches_traversal(o->src_index->cache_tree, names, info);
 +}
 +
 +static int index_pos_by_traverse_info(struct name_entry *names,
 +                                    struct traverse_info *info)
 +{
 +      struct unpack_trees_options *o = info->data;
 +      int len = traverse_path_len(info, names);
 +      char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */);
 +      int pos;
 +
 +      make_traverse_path(name, info, names);
 +      name[len++] = '/';
 +      name[len] = '\0';
 +      pos = index_name_pos(o->src_index, name, len);
 +      if (pos >= 0)
 +              BUG("This is a directory and should not exist in index");
 +      pos = -pos - 1;
 +      if (!starts_with(o->src_index->cache[pos]->name, name) ||
 +          (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name)))
 +              BUG("pos must point at the first entry in this directory");
 +      free(name);
 +      return pos;
 +}
 +
 +/*
 + * Fast path if we detect that all trees are the same as cache-tree at this
 + * path. We'll walk these trees in an iterative loop using cache-tree/index
 + * instead of ODB since we already know what these trees contain.
 + */
 +static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names,
 +                                struct name_entry *names,
 +                                struct traverse_info *info)
 +{
 +      struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
 +      struct unpack_trees_options *o = info->data;
 +      struct cache_entry *tree_ce = NULL;
 +      int ce_len = 0;
 +      int i, d;
 +
 +      if (!o->merge)
 +              BUG("We need cache-tree to do this optimization");
 +
 +      /*
 +       * Do what unpack_callback() and unpack_nondirectories() normally
 +       * do. But we walk all paths in an iterative loop instead.
 +       *
 +       * D/F conflicts and higher stage entries are not a concern
 +       * because cache-tree would be invalidated and we would never
 +       * get here in the first place.
 +       */
 +      for (i = 0; i < nr_entries; i++) {
 +              int new_ce_len, len, rc;
 +
 +              src[0] = o->src_index->cache[pos + i];
 +
 +              len = ce_namelen(src[0]);
 +              new_ce_len = cache_entry_size(len);
 +
 +              if (new_ce_len > ce_len) {
 +                      new_ce_len <<= 1;
 +                      tree_ce = xrealloc(tree_ce, new_ce_len);
 +                      memset(tree_ce, 0, new_ce_len);
 +                      ce_len = new_ce_len;
 +
 +                      tree_ce->ce_flags = create_ce_flags(0);
 +
 +                      for (d = 1; d <= nr_names; d++)
 +                              src[d] = tree_ce;
 +              }
 +
 +              tree_ce->ce_mode = src[0]->ce_mode;
 +              tree_ce->ce_namelen = len;
 +              oidcpy(&tree_ce->oid, &src[0]->oid);
 +              memcpy(tree_ce->name, src[0]->name, len + 1);
 +
 +              rc = call_unpack_fn((const struct cache_entry * const *)src, o);
 +              if (rc < 0) {
 +                      free(tree_ce);
 +                      return rc;
 +              }
 +
 +              mark_ce_used(src[0], o);
 +      }
 +      free(tree_ce);
 +      if (o->debug_unpack)
 +              printf("Unpacked %d entries from %s to %s using cache-tree\n",
 +                     nr_entries,
 +                     o->src_index->cache[pos]->name,
 +                     o->src_index->cache[pos + nr_entries - 1]->name);
 +      return 0;
  }
  
  static int traverse_trees_recursive(int n, unsigned long dirmask,
        void *buf[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
        struct name_entry *p;
 +      int nr_entries;
 +
 +      nr_entries = all_trees_same_as_cache_tree(n, dirmask, names, info);
 +      if (nr_entries > 0) {
 +              struct unpack_trees_options *o = info->data;
 +              int pos = index_pos_by_traverse_info(names, info);
 +
 +              if (!o->merge || df_conflicts)
 +                      BUG("Wrong condition to get here buddy");
 +
 +              /*
 +               * All entries up to 'pos' must have been processed
 +               * (i.e. marked CE_UNPACKED) at this point. But to be safe,
 +               * save and restore cache_bottom anyway to not miss
 +               * unprocessed entries before 'pos'.
 +               */
 +              bottom = o->cache_bottom;
 +              ret = traverse_by_cache_tree(pos, nr_entries, n, names, info);
 +              o->cache_bottom = bottom;
 +              return ret;
 +      }
  
        p = names;
        while (!p->mode)
@@@ -987,11 -810,6 +987,11 @@@ static struct cache_entry *create_ce_en
        return ce;
  }
  
 +/*
 + * Note that traverse_by_cache_tree() duplicates some logic in this function
 + * without actually calling it. If you change the logic here you may need to
 + * check and change there as well.
 + */
  static int unpack_nondirectories(int n, unsigned long mask,
                                 unsigned long dirmask,
                                 struct cache_entry **src,
@@@ -1184,11 -1002,6 +1184,11 @@@ static void debug_unpack_callback(int n
                debug_name_entry(i, names + i);
  }
  
 +/*
 + * Note that traverse_by_cache_tree() duplicates some logic in this function
 + * without actually calling it. If you change the logic here you may need to
 + * check and change there as well.
 + */
  static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
  {
        struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@@ -1476,7 -1289,6 +1476,7 @@@ int unpack_trees(unsigned len, struct t
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
  
 +      trace_performance_enter();
        memset(&el, 0, sizeof(el));
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
                        }
                }
  
 -              if (traverse_trees(len, t, &info) < 0)
 +              trace_performance_enter();
 +              ret = traverse_trees(len, t, &info);
 +              trace_performance_leave("traverse_trees");
 +              if (ret < 0)
                        goto return_failed;
        }
  
  
        ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index) {
 +              move_index_extensions(&o->result, o->src_index);
                if (!ret) {
 +                      if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
 +                              cache_tree_verify(&o->result);
                        if (!o->result.cache_tree)
                                o->result.cache_tree = cache_tree();
                        if (!cache_tree_fully_valid(o->result.cache_tree))
                                                  WRITE_TREE_SILENT |
                                                  WRITE_TREE_REPAIR);
                }
 -              move_index_extensions(&o->result, o->src_index);
                discard_index(o->dst_index);
                *o->dst_index = o->result;
        } else {
        o->src_index = NULL;
  
  done:
 +      trace_performance_leave("unpack_trees");
        clear_exclude_list(&el);
        return ret;
  
@@@ -1678,7 -1484,7 +1678,7 @@@ static int same(const struct cache_entr
        if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED)
                return 0;
        return a->ce_mode == b->ce_mode &&
 -             !oidcmp(&a->oid, &b->oid);
 +             oideq(&a->oid, &b->oid);
  }
  
  
@@@ -1810,7 -1616,7 +1810,7 @@@ static int verify_clean_subdirectory(co
                 * If we are not going to update the submodule, then
                 * we don't care.
                 */
 -              if (!sub_head && !oidcmp(&oid, &ce->oid))
 +              if (!sub_head && oideq(&oid, &ce->oid))
                        return 0;
                return verify_clean_submodule(sub_head ? NULL : oid_to_hex(&oid),
                                              ce, error_type, o);
                        if (verify_uptodate(ce2, o))
                                return -1;
                        add_entry(o, ce2, CE_REMOVE, 0);
 +                      invalidate_ce_path(ce, o);
                        mark_ce_used(ce2, o);
                }
                cnt++;
@@@ -2073,7 -1878,8 +2073,8 @@@ static int merged_entry(const struct ca
                invalidate_ce_path(old, o);
        }
  
-       do_add_entry(o, merge, update, CE_STAGEMASK);
+       if (do_add_entry(o, merge, update, CE_STAGEMASK) < 0)
+               return -1;
        return 1;
  }
  
@@@ -2098,8 -1904,6 +2099,8 @@@ static int keep_entry(const struct cach
                      struct unpack_trees_options *o)
  {
        add_entry(o, ce, 0, 0);
 +      if (ce_stage(ce))
 +              invalidate_ce_path(ce, o);
        return 1;
  }